Рассмотрим:
List<String> someList = new ArrayList<String>();
// add "monkey", "donkey", "skeleton key" to someList
for (String item : someList) {
System.out.println(item);
}
Как бы выглядел эквивалентный цикл for
без использования синтаксиса for each?
for (Iterator<String> i = someIterable.iterator(); i.hasNext();) {
String item = i.next();
System.out.println(item);
}
Обратите внимание, что если вам нужно использовать i.remove();
в цикле или каким-либо образом получить доступ к фактическому итератору, вы не можете использовать идиому for ( : )
, поскольку фактический итератор просто предполагается.
Как заметил Денис Буэно, этот код работает для любого объекта, реализующего интерфейс Iterable
.
Также, если правой стороной идиомы for (:)
является массив
, а не объект Iterable
, внутренний код использует счетчик индексов int и проверяет array.length
. См. Спецификацию языка Java.
The construct for each is also valid for arrays. e.g.
String[] fruits = new String[] { "Orange", "Apple", "Pear", "Strawberry" };
for (String fruit : fruits) {
// fruit is an element of the `fruits` array.
}
which is essentially equivalent of
for (int i = 0; i < fruits.length; i++) {
String fruit = fruits[i];
// fruit is an element of the `fruits` array.
}
So, overall summary:
[nsayer] The following is the longer form of what is happening:
for(Iterator
i = someList.iterator(); i.hasNext(); ) { String item = i.next(); System.out.println(item); } Note that if you need to use i.remove(); in your loop, or access the actual iterator in some way, you cannot use the for( : ) idiom, since the actual Iterator is merely inferred.
It's implied by nsayer's answer, but it's worth noting that the OP's for(..) синтаксис будет работать, когда "someList" - это anything that implements java.lang.Iterable -- it doesn't have to be a list, or some collection from java.util. Even your own types, therefore, can be used with this синтаксисом.
Цикл foreach
, добавленный в Java 5 (также называемый "улучшенным циклом for"), эквивалентен использованию java.util.Iterator
- это синтаксический сахар для того же самого. Поэтому при чтении каждого элемента по очереди и по порядку всегда следует выбирать foreach
, а не итератор, так как он более удобен и краток.
for(int i : intList) {
System.out.println("An element in the list: " + i);
}
Iterator<Integer> intItr = intList.iterator();
while(intItr.hasNext()) {
System.out.println("An element in the list: " + intItr.next());
}
Бывают ситуации, когда необходимо использовать Итератор
напрямую. Например, попытка удалить элемент при использовании foreach
может (будет?) привести к ConcurrentModificationException
.
foreach
против for
: Основные различияЕдинственное практическое различие между for
и foreach
заключается в том, что в случае с индексируемыми объектами у вас нет доступа к индексу. Пример, когда требуется базовый цикл for
:
for(int i = 0; i < array.length; i++) {
if(i < 5) {
// Do something special
} else {
// Do other stuff
}
}
Хотя вы могли бы вручную создать отдельную индексную int-переменную с помощью foreach
,
int idx = -1;
for(int i : intArray) {
idx++;
...
}
это не рекомендуется, поскольку variable-scope не идеален, а базовый цикл for
- это просто стандартный и ожидаемый формат для данного случая использования.
foreach
против for
: ПроизводительностьПри обращении к коллекциям foreach
значительно быстрее, чем обращение к массивам в базовом цикле for
. Однако при доступе к массивам - по крайней мере, к примитивным массивам и массивам-оберткам - доступ через индексы значительно быстрее.
Индексы на 23-40 процентов быстрее итераторов при доступе к массивам int
или Integer
. Вот результат работы тестового класса в нижней части этого сообщения, который суммирует числа в 100-элементном примитивно-интовом массиве (A - итератор, B - индекс):
[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 358,597,622 nanoseconds
Test B: 269,167,681 nanoseconds
B faster by 89,429,941 nanoseconds (24.438799231635727% faster)
[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 377,461,823 nanoseconds
Test B: 278,694,271 nanoseconds
B faster by 98,767,552 nanoseconds (25.666236154695838% faster)
[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 288,953,495 nanoseconds
Test B: 207,050,523 nanoseconds
B faster by 81,902,972 nanoseconds (27.844689860906513% faster)
[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 375,373,765 nanoseconds
Test B: 283,813,875 nanoseconds
B faster by 91,559,890 nanoseconds (23.891659337194227% faster)
[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 375,790,818 nanoseconds
Test B: 220,770,915 nanoseconds
B faster by 155,019,903 nanoseconds (40.75164734599769% faster)
[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 326,373,762 nanoseconds
Test B: 202,555,566 nanoseconds
B faster by 123,818,196 nanoseconds (37.437545972215744% faster)
Я также проверил это для массива Integer
, и индексы по-прежнему являются явным победителем, но только на 18-25 процентов быстрее.
Для списка
из интегралов
, однако, итераторы - явный победитель. Просто измените int-array в тест-классе на:
List<Integer> intList = Arrays.asList(new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100});
И внесите необходимые изменения в тест-функцию (int[]
на List<Integer>
, length
на ize()
, и т.д.):
[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 3,429,929,976 nanoseconds
Test B: 5,262,782,488 nanoseconds
A faster by 1,832,852,512 nanoseconds (34.326681820485675% faster)
[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,907,391,427 nanoseconds
Test B: 3,957,718,459 nanoseconds
A faster by 1,050,327,032 nanoseconds (26.038700083921256% faster)
[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,566,004,688 nanoseconds
Test B: 4,221,746,521 nanoseconds
A faster by 1,655,741,833 nanoseconds (38.71935684115413% faster)
[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,770,945,276 nanoseconds
Test B: 3,829,077,158 nanoseconds
A faster by 1,058,131,882 nanoseconds (27.134122749113843% faster)
[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 3,467,474,055 nanoseconds
Test B: 5,183,149,104 nanoseconds
A faster by 1,715,675,049 nanoseconds (32.60101667104192% faster)
[C:\java_code\]java TimeIteratorVsIndexIntList 1000000
Test A: 3,439,983,933 nanoseconds
Test B: 3,509,530,312 nanoseconds
A faster by 69,546,379 nanoseconds (1.4816434912159906% faster)
[C:\java_code\]java TimeIteratorVsIndexIntList 1000000
Test A: 3,451,101,466 nanoseconds
Test B: 5,057,979,210 nanoseconds
A faster by 1,606,877,744 nanoseconds (31.269164666060377% faster)
В одном тесте они почти эквивалентны, но с коллекциями побеждает итератор.
*Это сообщение основано на двух ответах, которые я написал на Stack Overflow:
Еще немного информации: https://stackoverflow.com/questions/2113216/which-is-more-efficient-a-for-each-loop-or-an-iterator
Я создал этот класс сравнения времени, необходимого для выполнения любых двух действий, после прочтения этого вопроса на Stack Overflow:
import java.text.NumberFormat;
import java.util.Locale;
/**
<P>{@code java TimeIteratorVsIndexIntArray 1000000}</P>
@see <CODE><A HREF="https://stackoverflow.com/questions/180158/how-do-i-time-a-methods-execution-in-java">https://stackoverflow.com/questions/180158/how-do-i-time-a-methods-execution-in-java</A></CODE>
**/
public class TimeIteratorVsIndexIntArray {
public static final NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
public static final void main(String[] tryCount_inParamIdx0) {
int testCount;
// Get try-count from a command-line parameter
try {
testCount = Integer.parseInt(tryCount_inParamIdx0[0]);
}
catch(ArrayIndexOutOfBoundsException | NumberFormatException x) {
throw new IllegalArgumentException("Missing or invalid command line parameter: The number of testCount for each test. " + x);
}
//Test proper...START
int[] intArray = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100};
long lStart = System.nanoTime();
for(int i = 0; i < testCount; i++) {
testIterator(intArray);
}
long lADuration = outputGetNanoDuration("A", lStart);
lStart = System.nanoTime();
for(int i = 0; i < testCount; i++) {
testFor(intArray);
}
long lBDuration = outputGetNanoDuration("B", lStart);
outputGetABTestNanoDifference(lADuration, lBDuration, "A", "B");
}
private static final void testIterator(int[] int_array) {
int total = 0;
for(int i = 0; i < int_array.length; i++) {
total += int_array[i];
}
}
private static final void testFor(int[] int_array) {
int total = 0;
for(int i : int_array) {
total += i;
}
}
//Test proper...END
//Timer testing utilities...START
public static final long outputGetNanoDuration(String s_testName, long l_nanoStart) {
long lDuration = System.nanoTime() - l_nanoStart;
System.out.println("Test " + s_testName + ": " + nf.format(lDuration) + " nanoseconds");
return lDuration;
}
public static final long outputGetABTestNanoDifference(long l_aDuration, long l_bDuration, String s_aTestName, String s_bTestName) {
long lDiff = -1;
double dPct = -1.0;
String sFaster = null;
if(l_aDuration > l_bDuration) {
lDiff = l_aDuration - l_bDuration;
dPct = 100.00 - (l_bDuration * 100.0 / l_aDuration + 0.5);
sFaster = "B";
}
else {
lDiff = l_bDuration - l_aDuration;
dPct = 100.00 - (l_aDuration * 100.0 / l_bDuration + 0.5);
sFaster = "A";
}
System.out.println(sFaster + " faster by " + nf.format(lDiff) + " nanoseconds (" + dPct + "% faster)");
return lDiff;
}
//Timer testing utilities...END
}
Вот ответ, который не предполагает знание Java итераторы. Он является менее точным, но это полезно для образования.
При программировании мы часто пишем код, который выглядит следующим образом:
char[] grades = ....
for(int i = 0; i < grades.length; i++) { // for i goes from 0 to grades.length
System.out.print(grades[i]); // Print grades[i]
}
Синтаксиса foreach позволяет эта общая картина должна быть написана в более естественный и менее синтаксически шумное.
for(char grade : grades) { // foreach grade in grades
System.out.print(grade); // print that grade
}
Кроме того, этот синтаксис действителен для объектов, таких как списки или наборы, которые не поддерживают индексации массива, но реализовать интерфейс java итератор.
Для каждого цикла в Java использует базового итератора механизм. Так что'ы идентичен следующим:
Iterator<String> iterator = someList.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
}
Это'ов подразумевается nsayer'ы ответ, но это's стоит отметить, что ОП's для(..) синтаксис будет работать, когда и"someList" это ничего, что реализации Java.яз.Итератор -- это не'т должен быть список, или некоторые коллекции из Java.утиль. Даже ваши собственные типы, поэтому можно использовать этот синтаксис.
Как определено в ПСБ для каждого цикла может иметь две формы:
итератор
, то перевод звучит так:Список<строка> В someList = новый ArrayList и Л;строка> В(); someList.добавить("в яблочко;); someList.добавить (на"Бал" - а); для (пункт строку : someList) { Системы.из.метод println(пункт); }
// ПЕРЕВЕДЕНО НА:
для(итератор в<строка> stringIterator = someList.итератор(); stringIterator.hasNext(); ) { Пункт строка = stringIterator.следующий(); Системы.из.метод println(пункт); }
Строка[] someArray = новая строка[2]; someArray[0] = "в яблочко;; someArray1 = "от шарика с";
для(строка, место № 2 : someArray) { Системы.из.код println(место № 2); }
// ПЕРЕВЕДЕНО НА: для (int я = 0; Я &л; someArray.длина; я++) { Строке место № 2 = someArray[я]; Системы.из.код println(место № 2); }
В Java 8 ввела потоков, которые выполняют, как правило, лучше. Мы можем использовать их в качестве:
someList.stream().forEach(System.out::println);
Arrays.stream(someArray).forEach(System.out::println);
Синтаксис цикла foreach является:
for (type obj:array) {...}
Пример:
String[] s = {"Java", "Coffe", "Is", "Cool"};
for (String str:s /*s is the array*/) {
System.out.println(str);
}
Выход:
Java
Coffe
Is
Cool
Предупреждение: Вы можете получить доступ к элементам массива с помощью цикла foreach, но вы не можете инициализировать их. Используйте оригинальную для
петли для этого.
Предупреждение: Вы должны соответствовать типу массива с другим объектом.
for (double b:s) // Invalid-double is not String
Если вы хотите редактировать элементов, используйте оригинальную для
петли так:
for (int i = 0; i < s.length-1 /*-1 because of the 0 index */; i++) {
if (i==1) //1 because once again I say the 0 index
s[i]="2 is cool";
else
s[i] = "hello";
}
Теперь, если мы дамп с консоли, мы получим:
hello
2 is cool
hello
hello
На Java "и для каждого" в конструкцию цикла позволит итерации двух типов объектов:
Т[]
(массивов любого типа)Ява.яз.Повторяемое в<Т>
В повторяемое в<Т>
интерфейс имеет только один метод: итератор в<Т> итератор()
. Это работы на объектах сбора тип в <Т>
потому что коллекция<П>
интерфейс расширяет повторяемое в<Т>
.
Концепция цикла foreach, как указано в Википедии приводятся ниже:
в отличие от других для петли конструкции, однако, циклы foreach обычно сохранить нет явного счетчика: они, по сути, говорит: "делай этого все в этом наборе-то", а не "У этого Х раз и". Это позволяет избежать потенциал --ОДН ошибок и делает код проще для чтения.
Таким образом, концепция цикла foreach описывает, что петли не использует никаких явных счетчик, который означает, что нет необходимости использовать индексы, чтобы пройти в список, таким образом, это избавляет пользователей от по одной ошибке. Для описания общей концепции это-за одной ошибки, давайте брать пример цикла, чтобы пройти в список с помощью индексов.
// In this loop it is assumed that the list starts with index 0
for(int i=0; i<list.length; i++){
}
Но, предположим, если список начинается с индекса 1, то этот цикл будет бросать исключение, как будет найден элемент с индексом 0 и эта ошибка называется на одну ошибку. Поэтому, чтобы избежать этого-по одной ошибке использовал понятие цикл foreach. Могут быть и другие преимущества, но это то, что я думаю, что это основная идея и преимущество использования цикла foreach.
for (Iterator<String> itr = someList.iterator(); itr.hasNext(); ) {
String item = itr.next();
System.out.println(item);
}
Используя старые версии Java, в том числе с Java 7 Вы можете использовать цикле foreach
следующим образом.
List<String> items = new ArrayList<>();
items.add("A");
items.add("B");
items.add("C");
items.add("D");
items.add("E");
for(String item : items){
System.out.println(item);
}
После это очень последний способ использования цикле foreach
в Ява 8
(цикл список с объекту
+ лямбда или эталонный метод)
//lambda
//Output : A,B,C,D,E
items.forEach(item->System.out.println(item));
//method reference
//Output : A,B,C,D,E
items.forEach(System.out::println);
Дополнительные сведения см. По этой ссылке.
В Java 8, они ввели по каждому элементу. Используя это списке карты может быть петельные.
Петля списка, используя для каждого
List<String> someList = new ArrayList<String>();
someList.add("A");
someList.add("B");
someList.add("C");
someList.forEach(listItem -> System.out.println(listItem))
или
someList.forEach(listItem-> {
System.out.println(listItem);
});
Петля карту, используя для каждого
Map<String, String> mapList = new HashMap<>();
mapList.put("Key1", "Value1");
mapList.put("Key2", "Value2");
mapList.put("Key3", "Value3");
mapList.forEach((key,value)->System.out.println("Key: " + key + " Value : " + value));
или
mapList.forEach((key,value)->{
System.out.println("Key : " + key + " Value : " + value);
});
Также обратите внимание, что использование "по объекту" и метод в исходный вопрос имеет некоторые ограничения, такие как невозможность удалить элементы из списка во время итерации.
Новый для-петли легче читать и устраняет необходимость для отдельный итератор, но это только действительно используемые в итерации проходит.
Альтернативы по каждому элементу для того, чтобы избежать вашу "каждому" по:
List<String> someList = new ArrayList<String>();
Вариант 1 (простой):
someList.stream().forEach(listItem -> {
System.out.println(listItem);
});
Вариант 2 (параллельное выполнение (быстрее)):
someList.parallelStream().forEach(listItem -> {
System.out.println(listItem);
});
Это добавит красоты в ваш код, удалив все основные циклы клаттер. Это дает чистый взгляд на ваш код, обосновано ниже.
Нормальный цикл for
:
void cancelAll(Collection<TimerTask> list) {
for (Iterator<TimerTask> i = list.iterator(); i.hasNext();)
i.next().cancel();
}
Используя для каждого:
void cancelAll(Collection<TimerTask> list) {
for (TimerTask t : list)
t.cancel();
}
за каждый является конструкцией за коллекцию, которая реализует итераторы. Помните, что ваш коллекция должен реализовать итераторы; в противном случае вы можете'т использовать его с для-каждого.
Следующая строка читается как "для каждого TimerTask т в списке.&и"
for (TimerTask t : list)
Существует меньше шансов для ошибок в случае для каждого. Вы Don'т придется беспокоиться об инициализации итератора или инициализация счетчика цикла и прекращения его действия (где есть простор для ошибок).
До Java 8, необходимо использовать следующее:
Iterator<String> iterator = someList.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
}
Однако, с введением потоков в Java 8 вы можете сделать то же самое в гораздо менее синтаксис. Например, для someList
вы можете сделать:
someList.stream().forEach(System.out::println);
Вы можете узнать больше о потоках здесь.
Это будет выглядеть что-то вроде этого. Очень crufty.
for (Iterator<String> i = someList.iterator(); i.hasNext(); )
System.out.println(i.next());
Есть хорошая рецензия на каждого в Солнце документацию.