Эффективность 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) {
...
}
Ответы
Ответ 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
Я считаю, что компилятор оптимизирован для запуска только один раз для записи в петле.