Основная идея новых API, вдохновленных функциональным программированием, состоит в том, чтобы выразить , что сделать вместо , как сделать это. Даже при использовании упрощенного цикла for-each,
for(Integer i: myList) System.out.println("Value::"+i);
это просто синтаксический сахар для инструкций «получить экземпляр Iterator
и повторно вызывать hasNext()
и next()
для него».
Напротив, при использовании
myList.forEach(i -> System.out.println("Value::"+i));
вы предоставляете действие, которое будет применено к каждому элементу, но не указываете, как это сделать. Реализация default
просто выполнит цикл на основе Iterator
, но фактические реализации Iterable
могут переопределить его для выполнения операции по-другому.
Обратите внимание, что многие реализации Iterator
выполняют проверки дважды, один раз в hasNext()
, затем снова в next()
, поскольку нет никакой гарантии, что вызывающий сначала hasNext()
сделал сначала, чтобы бросить NoSuchElementException
, если нет следующего элемента. Иногда это даже подразумевает сохранение дополнительного состояния в экземпляре Iterator
, чтобы запомнить, была ли уже выполнена определенная операция «выборка-следующая». Выделенная forEach
реализация может быть простой, более эффективной, но при этом проще в коде.
Например, ArrayList
выполняет цикл на основе int
-индекс, не создавая экземпляр Iterator
, Collections.emptyList()
ничего не делает, кроме проверки Consumer
против null
, TreeSet
соответственно. его поддержка TreeMap
пересекает входные ссылки, что намного проще, чем его реализация Iterator
, которая должна поддерживать операцию remove
и т. д.
Может ли выделенная реализация forEach
компенсировать издержки, связанные с конструкцией * 1043, если они есть (учтите, что не каждое лямбда-выражение создает новый объект ), не предсказуемо, и предположения относительно этого не должен управлять дизайном программного обеспечения. Чаще, различия в производительности незначительны.
Но могут быть и семантические различия. При использовании одной из коллекций, возвращаемых Collections.synchronized…
, цикл на основе Iterator
не может обеспечить гарантии согласованности, когда базовая коллекция модифицируется другим потоком. Приложению необходимо будет вручную заблокировать коллекцию, и ему нужно будет использовать правильный экземпляр объекта, например, если итерируемое значение равно subList
или subSet
. Напротив, специализированная forEach(Consumer)
правильно блокируется во время всей операции, тогда как сама операция так же проста, как делегирование методу forEach
источника, чтобы все же выполнить ее оптимальным способом в соответствии с фактической базовой коллекцией.