Java foreach эффективность - PullRequest
22 голосов
/ 25 мая 2009

У меня есть что-то вроде этого:

Map<String, String> myMap = ...;

for(String key : myMap.keySet()) {
   System.out.println(key);
   System.out.println(myMap.get(key)); 
}

Так ли myMap.keySet() вызывается один раз в цикле foreach ? Я думаю, что это так, но хочу ваше мнение.

Я хотел бы знать, если использование foreach таким образом (myMap.keySet()) влияет на производительность или эквивалентно этому:

Set<String> keySet = myMap.keySet();
for (String key : keySet) {
   ...
}

Ответы [ 6 ]

65 голосов
/ 25 мая 2009

Если вы хотите быть абсолютно уверенным, то скомпилируйте его в обоих направлениях, декомпилируйте и сравните. Я сделал это со следующим источником:

public void test() {
  Map<String, String> myMap = new HashMap<String, String>();

  for (String key : myMap.keySet()) {
    System.out.println(key);
    System.out.println(myMap.get(key));
  }

  Set<String> keySet = myMap.keySet();
  for (String key : keySet) {
    System.out.println(key);
    System.out.println(myMap.get(key));
  }
}

и когда я декомпилировал файл класса с Jad , я получаю:

public void test()
{
    Map myMap = new HashMap();
    String key;
    for(Iterator iterator = myMap.keySet().iterator(); iterator.hasNext(); System.out.println((String)myMap.get(key)))
    {
        key = (String)iterator.next();
        System.out.println(key);
    }

    Set keySet = myMap.keySet();
    String key;
    for(Iterator iterator1 = keySet.iterator(); iterator1.hasNext(); System.out.println((String)myMap.get(key)))
    {
        key = (String)iterator1.next();
        System.out.println(key);
    }
}

Так вот, ваш ответ. Он вызывается один раз с любой формой цикла for.

35 голосов
/ 25 мая 2009

Он вызывается только один раз. На самом деле он использует итератор, чтобы сделать трюк.

Кроме того, в вашем случае, я думаю, вы должны использовать

for (Map.Entry<String, String> entry : myMap.entrySet())
{
    System.out.println(entry.getKey());
    System.out.println(entry.getValue());
}

, чтобы избежать поиска на карте каждый раз.

9 голосов
/ 25 мая 2009

keySet() вызывается только один раз. «Расширенный цикл for» основан на интерфейсе Iterable, который он использует для получения Iterator, который затем используется для цикла. Даже невозможно итерировать по Set любым другим способом, так как нет индекса или чего-либо еще, с помощью чего вы могли бы получить отдельные элементы.

Однако, что вы действительно должны сделать, это полностью отказаться от такого рода проблем микрооптимизации - если у вас когда-либо возникают реальные проблемы с производительностью, вероятность составляет около 99%, что вы никогда не задумывались сами по себе.

7 голосов
/ 14 января 2010

Ответ находится в спецификации языка Java, декомпиляция не требуется :) Это то, что мы можем прочитать о расширенном для оператора :

Усовершенствованный оператор for имеет форма:

EnhancedForStatement:
        for ( VariableModifiersopt Type Identifier: Expression) Statement

Выражение должно иметь тип Iterable или это должно быть тип массива (§10.1) или время компиляции возникает ошибка.

Область локальной переменной объявлена в части FormalParameter расширенный оператор for (§14.14) Содержимое заявление

Значение расширенного for заявление дается переводом на базовое for утверждение.

Если тип Expression является подтип Iterable, тогда пусть I будет тип выражения Expression. iterator(). Расширенный оператор for эквивалентен к основному for утверждению форма:

for (I #i = Expression.iterator(); #i.hasNext(); ) {

        VariableModifiersopt Type Identifier = #i.next();
   Statement
}

Где #i - генерируется компилятором идентификатор, отличный от любого другие идентификаторы (генерируемые компилятором или иным образом), которые находятся в сфере действия (§6.3) в точке, где улучшено для заявление происходит.

В противном случае выражение обязательно имеет тип массива, T[]. Пусть L1 ... Lm быть (возможно, пустой) последовательностью этикетки, непосредственно предшествующие расширенное for заявление. Тогда Значение расширенного для заявления дается следующим основным for утверждение:

T[] a = Expression;
L1: L2: ... Lm:
for (int i = 0; i < a.length; i++) {
        VariableModifiersopt Type Identifier = a[i];
        Statement
}

Где a и i генерируются компилятором идентификаторы, которые отличаются от любых другие идентификаторы (генерируемые компилятором или иным образом), которые находятся в области Точка, где расширен для утверждения происходит.

В вашем случае myMap.keySet() возвращает подтип Iterable, поэтому ваш расширенный оператор for эквивалентен следующему базовому for утверждению:

for (Iterator<String> iterator = myMap.keySet().iterator(); iterator.hasNext();) {
   String key = iterator.next();

   System.out.println(key);
   System.out.println(myMap.get(key)); 
}

И myMap.keySet(), таким образом, вызывается только один раз.

5 голосов
/ 25 мая 2009

Да, он вызывается только один раз в любом случае

0 голосов
/ 25 мая 2009

Я считаю, что его компилятор оптимизирован для запуска только один раз для каждой записи цикла.

...