Подтвердить что ты не робот

Эффективность Java foreach

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

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) {
   ...
}
4b9b3361

Ответ 1

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

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);
    }
}

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

EnhancedForStatement:
        for ( VariableModifiersopt Type Identifier: Expression) Statement

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

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

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

Если тип Expression является подтип Iterable, то пусть I be тип выражения 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 и я генерируются компилятором идентификаторы, отличные от любых другие идентификаторы (генерируемые компилятором или иным образом), которые находятся в области точка, где расширенное выражение имеет место.

В вашем случае 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

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

Ответ 6

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