"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Os streams são sempre mais lentos que as coleções tradicionais para operações simples?

Os streams são sempre mais lentos que as coleções tradicionais para operações simples?

Publicado em 2024-11-08
Navegar:336

Are Streams Always Slower Than Traditional Collections for Simple Operations?

Java 8 Stream Performance vs. Coleções tradicionais

Recentemente, você se aventurou no Java 8 e conduziu um benchmark informal para comparar o desempenho de sua API Stream com as coleções clássicas. Seu teste envolve filtrar uma lista de números inteiros, extrair a raiz quadrada de números pares e armazenar os resultados em uma lista Dupla. No entanto, você está questionando a validade do seu teste e está ansioso para esclarecer as verdadeiras implicações de desempenho.

Avaliando o teste de referência

Seus resultados iniciais, que indicaram fluxos ser mais lento do que as cobranças, suscitou preocupações. Para garantir uma avaliação mais confiável, é essencial abordar possíveis erros e realizar um teste justo. Aqui estão algumas considerações:

  • Usando LinkedList: LinkedList não é uma escolha ideal para a lista de resultados, pois não possui acesso aleatório eficiente. Considere usar ArrayList.
  • Metodologia de benchmark: O benchmarking manual pode estar sujeito a imprecisões. Utilize uma estrutura de benchmarking como JMH (Java Microbenchmarking Harness) para fornecer medições mais precisas e confiáveis. avaliação de desempenho usando JMH e estratégias de benchmarking aprimoradas:
@OutputTimeUnit(TimeUnit.NANOSECONDS) @BenchmarkMode(Mode.AverageTime) @OperationsPerInvocation(StreamVsVanilla.N) classe pública StreamVsVanilla { público estático final int N = 10000; Lista estática sourceList = new ArrayList(); estático { for (int i = 0; i vanilla() { List resultado = new ArrayList(sourceList.size() / 2 1); for (Inteiro i: sourceList) { se (eu % 2 == 0){ resultado.add(Math.sqrt(i)); } } resultado de retorno; } @Referência lista pública stream() { retornar sourceList.stream() .filtro(i -> i % 2 == 0) .map(Matemática::sqrt) .collect(Collectors.toCollection( () -> new ArrayList(sourceList.size() / 2 1))); } }

Resultados:

Média de amostras do modo de referência Unidades de erro médio StreamVsVanilla.stream média 10 17,588 0,230 ns/op StreamVsVanilla.vanilla avgt 10 10,796 0,063 ns/op
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@OperationsPerInvocation(StreamVsVanilla.N)
public class StreamVsVanilla {
    public static final int N = 10000;

    static List sourceList = new ArrayList();
    static {
        for (int i = 0; i  vanilla() {
        List result = new ArrayList(sourceList.size() / 2   1);
        for (Integer i : sourceList) {
            if (i % 2 == 0){
                result.add(Math.sqrt(i));
            }
        }
        return result;
    }

    @Benchmark
    public List stream() {
        return sourceList.stream()
                .filter(i -> i % 2 == 0)
                .map(Math::sqrt)
                .collect(Collectors.toCollection(
                    () -> new ArrayList(sourceList.size() / 2   1)));
    }
}

Findings

Ao contrário dos resultados iniciais, o benchmark JMH mostra claramente que a abordagem de coleta tradicional é significativamente mais rápida do que a abordagem de fluxo neste particular cenário.
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@OperationsPerInvocation(StreamVsVanilla.N)
public class StreamVsVanilla {
    public static final int N = 10000;

    static List sourceList = new ArrayList();
    static {
        for (int i = 0; i  vanilla() {
        List result = new ArrayList(sourceList.size() / 2   1);
        for (Integer i : sourceList) {
            if (i % 2 == 0){
                result.add(Math.sqrt(i));
            }
        }
        return result;
    }

    @Benchmark
    public List stream() {
        return sourceList.stream()
                .filter(i -> i % 2 == 0)
                .map(Math::sqrt)
                .collect(Collectors.toCollection(
                    () -> new ArrayList(sourceList.size() / 2   1)));
    }
}

Conclusão

Com base nesses melhores resultados de benchmarking, podemos concluir que:

Streams não são inerentemente mais lentos que coleções. No entanto, sua sobrecarga pode superar os benefícios em certos casos de uso, como operações simples de filtragem e mapeamento.Streams oferecem vantagens significativas em termos de simplicidade e capacidade de manutenção do código. Eles simplificam os pipelines de processamento de dados e reduzem o código clichê.

Para caminhos de código críticos para o desempenho, é sempre aconselhável realizar benchmarking completo e considerar os requisitos específicos do seu aplicativo.

    Tutorial mais recente Mais>

    Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

    Copyright© 2022 湘ICP备2022001581号-3