Pergunta

O que, se houver, é a diferença de desempenho entre as duas voltas seguintes?

for (Object o: objectArrayList) {
    o.DoSomething();
}

e

for (int i=0; i<objectArrayList.size(); i++) {
    objectArrayList.get(i).DoSomething();
}
Foi útil?

Solução

A partir do item 46 em Effective Java por Joshua Bloch:

A for-each loop, introduzido em Versão 1.5, se livrar da desordem ea possibilidade de erro por escondendo a variável iteração ou índice completamente. A expressão resultante aplica-se igualmente às coleções e matrizes:

// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
    doSomething(e);
}

Quando você vê os dois pontos (:), lê-lo como "no." Assim, o circuito acima como lê “Para cada elemento e em elementos”. Nota que não há nenhuma penalidade de desempenho para usando o para-cada ciclo, até mesmo para matrizes. Na verdade, ele pode oferecer uma ligeira vantagem de desempenho sobre um ordinário por ciclo, em algumas circunstâncias, como ele calcula o limite do índice de matriz apenas uma vez. Enquanto você pode fazer isso mão (ponto 45), os programadores não sempre fazê-lo.

Outras dicas

Todos estes laços fazer exatamente o mesmo, eu só quero mostrar estes antes de atirar em meus dois centavos.

Em primeiro lugar, a maneira clássica de ciclo através da lista:

for(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ }

Em segundo lugar, a forma preferida, já que é menos propenso a erros (quantas vezes você fez o "oops, misturado as variáveis ??i e j nestes laços dentro de loops de" coisa?).

for(String s : strings) { /* do something using s */ }

Em terceiro lugar, o micro-otimizado para loop:

int size = strings.size();
for(int i=0;++i<=size;) { /* do something using strings.get(i) */ }

Agora, os reais e dois centavos: Pelo menos quando eu estava testando estes, o terceiro foi o mais rápido quando milissegundos contando com o tempo que levou para cada tipo de laço com uma operação simples nele repetido algumas milhão de vezes - este foi usando Java 5 com jre1.6u10 no Windows no caso de alguém está interessado.

Enquanto isso, pelo menos, parece ser de modo que o terceiro é o mais rápido, você realmente deve se perguntar se você quer correr o risco de implementar essa otimização olho mágico em todos os lugares em seu código looping, já que desde que eu vi, real looping não é geralmente a parte mais demorada de qualquer programa real (ou talvez eu só estou trabalhando no campo errado, quem sabe). E também como mencionei no pretexto para o Java para-cada loop (alguns se referem a ele como circuito Iterator e outros como for-em malha ) que são menos propensos a bater que um erro estúpido em particular quando usá-lo. E antes de discutir como isso pode até mesmo ser mais rápido do que os outros, lembre-se que javac faz bytecode não otimizar em tudo (bem, quase em tudo de qualquer maneira), ele só compila-lo.

Se você estiver em micro-otimização embora e / ou seus usa software lotes de loops recursivo e tal, então você pode estar interessado no terceiro tipo loop. Apenas lembre-se de comparar o seu software bem antes e depois de alterar o loops que você tem que esse estranho, um micro-otimizado.

A geralmente devem ser de preferência para-cada ciclo. A abordagem "pegar" pode ser mais lenta se a implementação lista que você está usando não suporta acesso aleatório. Por exemplo, se um LinkedList é usado, você seria incorrer em um custo de passagem, enquanto que o para-cada abordagem usa um iterador que mantém o controle de sua posição na lista. Mais informações sobre o nuances do para-cada loop.

Eu acho que o artigo agora está aqui: novo local

O link mostrado aqui estava morto.

Bem, impacto no desempenho é em grande parte insignificante, mas não é zero. Se você olhar para JavaDoc da interface de RandomAccess:

Como uma regra de ouro, uma lista implementação deve implementar esta interface se, para os casos típicos de a classe, este loop:

for (int i=0, n=list.size(); i < n; i++)
    list.get(i);

corre mais rápido do que este loop:

for (Iterator i=list.iterator(); i.hasNext();)
      i.next();

E para-cada loop está usando a versão com iterator, então para ArrayList por exemplo, for-each loop não é mais rápido.

Parece haver uma diferença infelizmente.

Se você olhar para o código gerado bytes para ambos os tipos de loops, eles são diferentes.

Aqui está um exemplo do código-fonte Log4j.

Em /log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java temos uma classe interna estática chamada Log4jMarker que define:

    /*
     * Called from add while synchronized.
     */
    private static boolean contains(final Marker parent, final Marker... localParents) {
        //noinspection ForLoopReplaceableByForEach
        for (final Marker marker : localParents) {
            if (marker == parent) {
                return true;
            }
        }
        return false;
    }

Com laço padrão:

  private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
    Code:
       0: iconst_0
       1: istore_2
       2: aload_1
       3: arraylength
       4: istore_3
       5: iload_2
       6: iload_3
       7: if_icmpge     29
      10: aload_1
      11: iload_2
      12: aaload
      13: astore        4
      15: aload         4
      17: aload_0
      18: if_acmpne     23
      21: iconst_1
      22: ireturn
      23: iinc          2, 1
      26: goto          5
      29: iconst_0
      30: ireturn

Com for-each:

  private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
    Code:
       0: aload_1
       1: astore_2
       2: aload_2
       3: arraylength
       4: istore_3
       5: iconst_0
       6: istore        4
       8: iload         4
      10: iload_3
      11: if_icmpge     34
      14: aload_2
      15: iload         4
      17: aaload
      18: astore        5
      20: aload         5
      22: aload_0
      23: if_acmpne     28
      26: iconst_1
      27: ireturn
      28: iinc          4, 1
      31: goto          8
      34: iconst_0
      35: ireturn

O que há com que a Oracle?

Eu tentei isso com Java 7 e 8 no Windows 7.

É sempre melhor usar o iterador em vez de indexação. Isso ocorre porque iterador é mais provável optimzied para a implementação da lista, enquanto indexados (get chamada) pode não ser. Por exemplo LinkedList é uma lista, mas a indexação através de seus elementos será mais lenta do que a iteração usando o iterador.

foreach faz a intenção de seu código mais clara e que normalmente é preferível a uma melhoria de velocidade muito menor -. Se houver

Sempre que vejo um loop indexada eu tenho que analisá-lo um pouco mais para se certificar de que faz o que eu pensar que faz Por exemplo Será que começar do zero, não é incluir ou excluir o ponto final etc.?

A maioria do meu tempo parece ser gasto lendo o código (que eu escrevi ou alguém escreveu) e clareza é quase sempre mais importante do que o desempenho. Sua fácil demitir o desempenho nestes dias, porque Hotspot faz um trabalho incrível.

O código a seguir:

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

interface Function<T> {
    long perform(T parameter, long x);
}

class MyArray<T> {

    T[] array;
    long x;

    public MyArray(int size, Class<T> type, long x) {
        array = (T[]) Array.newInstance(type, size);
        this.x = x;
    }

    public void forEach(Function<T> function) {
        for (T element : array) {
            x = function.perform(element, x);
        }
    }
}

class Compute {
    int factor;
    final long constant;

    public Compute(int factor, long constant) {
        this.factor = factor;
        this.constant = constant;
    }

    public long compute(long parameter, long x) {
        return x * factor + parameter + constant;
    }
}

public class Main {

    public static void main(String[] args) {
        List<Long> numbers = new ArrayList<Long>(50000000);
        for (int i = 0; i < 50000000; i++) {
            numbers.add(i * i + 5L);
        }

        long x = 234553523525L;

        long time = System.currentTimeMillis();
        for (int i = 0; i < numbers.size(); i++) {
            x += x * 7 + numbers.get(i) + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
        x = 0;
        time = System.currentTimeMillis();
        for (long i : numbers) {
            x += x * 7 + i + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
        x = 0;
        numbers = null;
        MyArray<Long> myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
        for (int i = 0; i < 50000000; i++) {
            myArray.array[i] = i * i + 3L;
        }
        time = System.currentTimeMillis();
        myArray.forEach(new Function<Long>() {

            public long perform(Long parameter, long x) {
                return x * 8 + parameter + 5L;
            }
        });
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(myArray.x);
        myArray = null;
        myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
        for (int i = 0; i < 50000000; i++) {
            myArray.array[i] = i * i + 3L;
        }
        time = System.currentTimeMillis();
        myArray.forEach(new Function<Long>() {

            public long perform(Long parameter, long x) {
                return new Compute(8, 5).compute(parameter, x);
            }
        });
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(myArray.x);
    }
}

Dá seguinte saída no meu sistema:

224
-699150247503735895
221
-699150247503735895
220
-699150247503735895
219
-699150247503735895

Estou correndo Ubuntu 12.10 alpha com OracleJDK 1.7 atualização 6.

Em geral HotSpot otimiza um monte de indireções e operações reduntant simples, de modo em geral você não deve se preocupar com eles, a menos que há um monte deles em seqence ou eles são fortemente aninhados.

Por outro lado, ser indexada em LinkedList é muito mais lento do que chamar o próximo na iterador para LinkedList assim você pode evitar que acerto de desempenho, mantendo a legibilidade quando você usa iteradores (explícita ou implicitamente na for-each loop).

Mesmo com algo como um ArrayList ou Vector, onde "pegar" é uma pesquisa de matriz simples, o segundo ciclo ainda tem sobrecarga adicional que o primeiro não. Eu esperava que fosse um pouco mais lento do que o primeiro.

A única maneira de saber ao certo se a referência, e mesmo que é tão simples como pode soar . O compilador JIT pode fazer coisas muito inesperados ao seu código.

Aqui está uma análise breve da diferença colocar para fora pela equipe de desenvolvimento Android:

https://www.youtube.com/watch?v=MZOf3pOAM6A

O resultado é que há é a diferença, e em ambientes muito contido com listas muito grandes que poderia ser uma diferença notável. Em seus testes, o para cada loop levou o dobro do tempo. No entanto, os testes foi mais de uma ArrayList de 400.000 inteiros. A diferença real por elemento na matriz foi de 6 microssegundos . Eu não testei e eles não disse, mas eu esperaria a diferença para ser ligeiramente maior uso de objetos em vez de primitivas, mas ainda assim a menos que você está construindo o código da biblioteca, onde você não tem idéia da escala do que você será solicitado para repetir, eu acho que a diferença não vale a pena se estressar com.

Pelo nome da variável objectArrayList, presumo que é uma instância de java.util.ArrayList. Nesse caso, a diferença de desempenho seria imperceptível.

Por outro lado, se é um exemplo de java.util.LinkedList, a segunda abordagem será muito mais lenta como o List#get(int) é uma operação O (n).

Assim, a primeira abordagem é sempre preferível a menos que o índice é necessário para a lógica no circuito.

1. for(Object o: objectArrayList){
    o.DoSomthing();
}
and

2. for(int i=0; i<objectArrayList.size(); i++){
    objectArrayList.get(i).DoSomthing();
}

Tanto faz o mesmo, mas para fácil e segura utilização de programação para-cada, há possibilidades de erro propenso na segunda maneira de usar.

É estranho que ninguém tenha mencionado o óbvio - Aloca memória foreach (na forma de um iterador), enquanto que a normal para loop não alocar qualquer memória. Para os jogos no Android, este é um problema, porque isso significa que o coletor de lixo será executado periodicamente. Em um jogo que você não quer que o coletor de lixo para executar ... NUNCA. Portanto, não use foreach loops em seu draw (ou tornar) método.

aceitado resposta responde à pergunta, para além do caso excepcional de ArrayList ...

Uma vez que a maioria dos desenvolvedores dependem de ArrayList (pelo menos eu acredito que sim)

Então, eu sou obrigado a acrescentar a resposta correta aqui.

Direto da documentação do desenvolvedor: -

O reforçada para o laço (também por vezes conhecido como "para-cada" ciclo) podem ser usados ??para conjuntos que implementam a interface Iterable e para matrizes. Com coleções, um iterador é alocado para fazer chamadas interface para hasNext () e next (). Com um ArrayList, um loop contados escrita à mão é de cerca de 3x mais rápido (com ou sem JIT), mas para outras coleções do reforçada para a sintaxe de loop será exatamente equivalente ao uso iterador explícito.

Existem várias alternativas para a iteração através de um array:

static class Foo {
    int mSplat;
}

Foo[] mArray = ...

public void zero() {
    int sum = 0;
    for (int i = 0; i < mArray.length; ++i) {
        sum += mArray[i].mSplat;
    }
}

public void one() {
    int sum = 0;
    Foo[] localArray = mArray;
    int len = localArray.length;

    for (int i = 0; i < len; ++i) {
        sum += localArray[i].mSplat;
    }
}

public void two() {
    int sum = 0;
    for (Foo a : mArray) {
        sum += a.mSplat;
    }
}

zero () é mais lento, porque o JIT não pode ainda optimizar longe o custo de obtenção do comprimento da matriz, uma vez para cada iteração através do laço.

(um) é mais rápido. Ele puxa tudo para fora em variáveis ??locais, evitando as pesquisas. Apenas as ofertas comprimento da matriz um benefício de desempenho.

dois () é mais rápida para dispositivos sem JIT, e indistinguível de um () para dispositivos com um JIT. Ele usa o avançado para a sintaxe de loop introduzido na versão 1.5 da linguagem de programação Java.

Assim, você deve usar o loop for aprimorado por padrão, mas considere um loop contados escrita à mão para a iteração ArrayList de desempenho crítico.

public class FirstJavaProgram {

    public static void main(String[] args) 
    {
        int a[]={1,2,3,45,6,6};

// Method 1: this is simple way to print array 

        for(int i=0;i<a.length;i++) 
        { 
            System.out.print(a[i]+" ");
        }

// Method 2: Enhanced For loop

        for(int i:a)
        {
            System.out.print(i+" ");
        }
    }
}

Sim, variante for-each é mais rápido que do que index-based-for-loop normal.

variante for-each usa iterator. Então travessia é mais rápido do que ciclo for normal, que é baseado índice.
Isso ocorre porque iterator são otimizados para atravessar, porque ele está apontando para , pouco antes do próximo elemento e logo após o elemento anterior . Uma das razões por ser index-based-for-loop a ser lento é que, ele tem que calcular e movimento para a posição de elemento de cada vez que não é com o iterator.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top