Peek () действительно, чтобы увидеть элементы, проходящие мимо определенной точки в конвейере - PullRequest
0 голосов
/ 28 января 2019

Моя проблема наиболее простым и понятным способом:

Согласно JavaDoc :

Peek() метод существует главным образом для поддержки отладки, где вы хотитеувидеть элементы, когда они протекают через определенную точку в трубопроводе.

У меня есть труба 10 метров и вНа расстоянии 3 и 7 метров от входной головки у меня есть два маркера [aka peek()] для проверки / отладки моих элементов.

Теперь от конца ввода iЯ даю ввод 1,2,3,4,5.

В точке x = 4 метра , у меня есть filter(), который фильтрует все элементы меньше , чем и равно 3.

Теперь, согласно документу Java, я должен видеть, что произошло с моим входом в конвейер на расстоянии 3 и 7 метров.

Вывод на marker1 на расстояние 3 (.peek()) должно быть 1,2,3,4,5 не должно быть ??и вывод на marker2 на дистанции 7 , очевидно, должен быть 4,5.

Но на самом деле этого не происходит, выход поступает на 1-й рынок (.peek()) просто 1,2,3 и на 2-й день наступает 4,5.


Код, который я выполнил для проверки моей теории:

final List<Integer> IntList=
    Stream.of(1, 2, 3, 4, 5)
    .peek(it -> System.out.println("Before Filtering "+it)) // should print 1,2,3,4,5
    .filter(it -> it >= 3)
    .peek(it -> System.out.println("After Filtering: "+it)) //should print 4,5
    .collect(Collectors.toList());

Фактический вывод:

Before Filtering 1
Before Filtering 2
Before Filtering 3
After Filtering: 3
Before Filtering 4
After Filtering: 4
Before Filtering 5
After Filtering: 5

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

    Before Filtering 1
    Before Filtering 2
    Before Filtering 3
    Before Filtering 4
    Before Filtering 5
    After Filtering: 4
    After Filtering: 5

Если .peek() не только для отладки в определенной точке конвейера, то это определение неоднозначно.

Извините за мою историюТрубка, я так думал, что смог бы лучше объяснить, что хочу спросить.

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Ответ Андрея Ахметова правильный, но я хочу добавить к нему, потому что здесь есть два вопроса.Один из них - это общая проблема семантики потоковых конвейеров, и именно об этом ваш вопрос.Вторичный вопрос о значении и ограничениях peek().

К основному вопросу - который не имеет ничего общего с peek(), за исключением того, как вы наблюдаете состояние происходящего- Ваша интуиция о потоках просто неверна.Нет оснований полагать, что:

collection.stream()
          .filter(x -> x.foo() > 3)
          .map(X::toBar)
          .forEach(b -> System.out.println("Bar: " + b);

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

Причиной нестабильности языка peek() является то, что для конвейеров типа:

int size = collection.stream()
                     .map(...)
                     .peek(...)
                     .count()

Мы можем оценить ответ без выполнения любого отображения (так как map () известнобыть сохраняющей размер операцией.) Требование всегда предоставлять элементы в точках peek() подорвало бы ряд полезных оптимизаций.Таким образом, реализация может свободно исключить всю середину конвейера, если сможет доказать, что это не повлияет на ответ .(Это может привести к меньшему количеству побочных эффектов , но если вы так сильно беспокоитесь о побочных эффектах, возможно, вам не следует использовать потоки.)

0 голосов
/ 28 января 2019

Нет.Потоки могут оцениваться лениво по мере необходимости, и порядок операций не является строго определенным, особенно когда вы peek() ing.Это позволяет API потоков поддерживать очень большие потоки без значительных затрат времени и памяти, а также обеспечивает некоторые упрощения реализации.В частности, отдельная стадия конвейера не должна быть полностью оценена до следующего этапа.

Предположим, насколько бесполезен следующий код, учитывая ваши предположения:

IntStream.range(1, 1000000).skip(5).limit(10).forEach(System::println);

Поток начинается с миллиона элементов и заканчивается 10. Если мы оценим каждую стадию полностью, наше промежуточное значение составит 1 миллион, 999995 и 10 элементов соответственно.

В качестве второго примера, следующий поток не может быть оценен за этап за раз (поскольку IntStream.generate возвращает бесконечный поток):

IntStream.generate(/* some supplier */).limit(10).collect(Collectors.toList());

Ваш конвейердействительно проходит каждый отдельный элемент через первый peek, а затем только подмножество через второй peek.Тем не менее, конвейер выполняет эту оценку в мажорном элементе, а не в мажорном порядке: он оценивает канал для 1, отбрасывая его в фильтре, затем 2. Как только он оценивает канал для 3, он проходит фильтр, таким образом, оба просмотраоператор выполнить, и то же самое происходит для 4 и 5.

...