Java: потребительский интерфейс в потоке не работает должным образом

У меня есть 2 утверждения, я ожидал, что они должны "печатать" тот же результат:

Arrays.stream("abc".split("")).forEach(System.out::println);//first
Arrays.stream("abc".split("")).peek(new Consumer<String>() {//second
    @Override
    public void accept(String s) {
        System.out.println(s);//breakpoint
    }
});

На самом деле, первое выражение напечатает

a
b
c

Хорошо, но второй оператор ничего не печатает. Я попытался установить точку останова в строке «//точка останова» внутри IntelliJ, но она не сработала.

Итак, как мне изменить второй оператор, чтобы использовать «заглянуть», поскольку он создает новый поток при обработке каждого элемента с использованием «Потребителя»?

Большое спасибо.


person Troskyvs    schedule 24.11.2018    source источник
comment
Peek — это промежуточная операция.   -  person ema    schedule 24.11.2018


Ответы (4)


Stream.peek, как указано в javadocs API, предназначен в основном для целей отладки, и выполнение каких-либо операций обновления в потоке во время операции просмотра не рекомендуется.

Например, вы можете проверить состояние промежуточного потока с помощью следующего кода и узнать, к чему это в итоге приведет:

Arrays.stream("acb".split(""))
      .peek(System.out::println) // print a  c  b 
      .sorted()
      .forEach(System.out::println); // print a  b  c

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

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

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

person Naman    schedule 24.11.2018

peek() не является терминальной операцией, вам нужно добавить любую терминальную операцию, чтобы заставить работать просмотр, например.

Arrays.stream("abc".split("")).peek(new Consumer<String>() { //second
    @Override
    public void accept(String s) {
        System.out.println(s);//breakpoint
    }
}).count();
person Evgeniy Dorofeev    schedule 24.11.2018
comment
не рекомендуется выполнять операции короткого замыкания, такие как count с просмотром, так как когда операция оптимизирована, action не будет вызываться. - person Naman; 24.11.2018

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

Например: если вы добавите операцию терминала count() во второй поток, вы получите ожидаемый результат.

Примечание. Вы получили вывод для первого потока, потому что forEach() является терминальной операцией.

person Nicholas K    schedule 24.11.2018

Потоковые операции делятся на промежуточные (Stream-producing) операции и терминальные (value- or side-effect-producing) операции. Промежуточные операции всегда lazy. Таким образом, Steam начнет выполнение конвейера операций, как только получит какую-либо терминальную операцию. В вашем первом случае forEach - это операция терминала, поэтому поток выполняется. Но во втором случае последней операцией в конвейере является peek(), которая не является терминальной операцией.

person Amit Bera    schedule 24.11.2018