it-swarm.com.ru

Поток и ленивая оценка

Я читаю из Java 8 API абстракции потока, но я не очень хорошо понимаю это предложение:

Промежуточные операции возвращают новый поток. Они всегда ленивы; выполнение промежуточной операции, такой как filter (), фактически не выполняет никакой фильтрации, но вместо этого создает новый поток, который при прохождении содержит элементы исходного потока, которые соответствуют данному предикату. Обход источника конвейера не начинается, пока не будет выполнена терминальная операция конвейера.

Когда операция фильтрации создает новый поток, содержит ли этот поток фильтруемый элемент? Кажется, что он понимает, что поток содержит элементы только тогда, когда он пересекается, то есть с помощью операции терминала. Но чем же содержит отфильтрованный поток? Я не совсем понимаю!!!

33
xdevel2000

Это означает, что фильтр применяется только во время работы терминала. Подумайте о чем-то вроде этого:

public Stream filter(Predicate p) {
    this.filter = p; // just store it, don't apply it yet
    return this; // in reality: return a new stream
}
public List collect() {
    for (Object o : stream) {
        if (filter.test(o)) list.add(o);
    }
    return list;
}

(Это не компилирует и упрощает реальность, но принцип есть)

50
assylias

Потоки ленивы, потому что промежуточные операции не оцениваются, если не вызвана терминальная операция.

Каждая промежуточная операция создает новый поток, сохраняет предоставленную операцию/функцию и возвращает новый поток.

Конвейер накапливает эти вновь созданные потоки.

Время, когда вызывается операция терминала, начинается прохождение потоков, и связанная функция выполняется одна за другой.

Параллельные потоки не оценивают потоки "один за другим" (в конечной точке). Операции выполняются одновременно, в зависимости от доступных ядер.

12
Nisarg Patil

Мне кажется, что промежуточная операция не совсем ленивая

List<String> l3 = new ArrayList<String>();
        l3.add("first");
        l3.add("second");
        l3.add("third");
        l3.add("fouth");
        l3.add("fith");
        l3.add("sixth");

        List<String> test3 = new ArrayList<String>();
        try {
            l3.stream().filter(s -> { l3.clear(); test3.add(s); return true;}).forEach(System.out::println);
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("!!! ");
            System.out.println(test3.stream().reduce((s1, s2) -> s1 += " ;" + s2).get());
        }

Задачи и результаты:

  first
    null
    null
    null
    null
    null
    Java.util.ConcurrentModificationException
        at Java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.Java:1380)
        at Java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.Java:481)
        at Java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.Java:471)
        at Java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.Java:151)
        at Java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.Java:174)
        at Java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.Java:234)
        at Java.util.stream.ReferencePipeline.forEach(ReferencePipeline.Java:418)
        at test.TestParallel.main(TestParallel.Java:69)
    !!! 

    first ;null ;null ;null ;null ;null

Похоже, количество наборов итераций при создании потока, но получение нового элемента потока ленивым.

Сравните с циклом со счетчиком:

public static void main(String[] args) {
    List<Integer> list = new ArrayList<>(); 
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    int i = 0;
    while (i < list.size()) {
        System.out.println(list.get(i++));
        list.clear();
    }
}

Результат:

1

Только одна ожидаемая итерация. Я согласен, что проблема в поведении исключения в потоках, но я думаю, что ленивый означает получить данные (или выполнить какое-либо действие) только тогда, когда я прошу некоторый объект сделать это; и количество данных также данные.

1
Андрей Палкин