Почему в Java 8 введен цикл iterable.forEach (), хотя он есть для каждого? - PullRequest
0 голосов
/ 25 июня 2018

Я не могу понять, почему java 8 имеет цикл forEach и принимает в качестве параметра функциональный интерфейс Consumer. Несмотря на то, что мы можем выполнить одну и ту же задачу, используя традиционный для каждого цикла, не создавая дополнительных издержек для создания класса, он реализует это из Consumer и реализует метод, а затем передает это как ссылку на forEach (). Хотя есть лямбда-выражение, чтобы сделать его коротким.

Q1- почему iterable.forEach ()?

Q2. Где его использовать?

Q3. какой из них быстрее традиционного для каждой из Java 8 forEach ()?

Пример:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.lang.Integer;

public class ForEachExample {

    public static void main(String[] args) {

        //creating sample Collection
        List<Integer> myList = new ArrayList<Integer>();
        for(int i=0; i<10; i++) myList.add(i);

        //traversing using Iterator
        Iterator<Integer> it = myList.iterator();
        while(it.hasNext()){
            Integer i = it.next();
            System.out.println("Iterator Value::"+i);
        }

        //traversing through forEach method of Iterable with anonymous class
        myList.forEach(new Consumer<Integer>() {

            public void accept(Integer t) {
                System.out.println("forEach anonymous class Value::"+t);
            }

        });

        //traversing with Consumer interface implementation
        MyConsumer action = new MyConsumer();
        myList.forEach(action);

    }

}

//Consumer implementation that can be reused
**class MyConsumer implements Consumer<Integer>{
    public void accept(Integer t) {
        System.out.println("Consumer impl Value::"+t);
    }
}**

Ответы [ 4 ]

0 голосов
/ 25 июня 2018

Основная идея новых 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 источника, чтобы все же выполнить ее оптимальным способом в соответствии с фактической базовой коллекцией.

0 голосов
/ 25 июня 2018

Q1 - почему iterable.forEach ()?

Это введено для сокращения строк кода. Ваш цикл может выглядеть следующим образом как старый и новый путь

по-старому

   for (Integer t : myList) {
        System.out.println("forEach old way Value::"+t);
    }

новый способ

   myList.forEach(t ->System.out.println("forEach lambda way::"+t));

Q2. Где это использовать?

В любом месте, где вы хотите перебрать коллекцию.

Q3. какой из них более традиционный для каждой из Java 8 forEach ()?

Устаревший и новый способ - большой разницы в производительности нет. Но если вы используете поток для итерации, у него есть задержка, чем старая для каждого.

0 голосов
/ 25 июня 2018

Это просто вкусная проблема.Если вы хотите что-то более функциональное, используйте forEach, но в вашем случае традиционный цикл for-each хорош.На самом деле есть две вещи, которые for-loop поддерживает, но forEach нет:

  1. ключевые слова управления потоком, такие как continue, break, return
  2. Checked exception
0 голосов
/ 25 июня 2018

forEach был создан для использования лямбда-синтаксиса:

myList.forEach(System.out::println);

или

myList.forEach(e->System.out.println(e));

В этом вся цель функционального интерфейса

...