Учитывая элементы списка, которые добавляются после создания отфильтрованного потока - PullRequest
0 голосов
/ 22 декабря 2018

Учитывая следующий код:

List<String> strList = new ArrayList<>(Arrays.asList("Java","Python","Php"));

Stream<String> jFilter = strList.stream().filter(str -> str.startsWith("J"));

strList.add("JavaScript"); // element added after filter creation
strList.add("JQuery"); // element added after filter creation

System.out.println(Arrays.toString(jFilter.toArray())); 

, который выводит:

[Java, JavaScript, JQuery]

Почему JavaScript и JQuery появляются в отфильтрованном результате, даже если они были добавлены после созданияотфильтрованный поток?

Ответы [ 6 ]

0 голосов
/ 23 декабря 2018

Метод toArray является терминальной операцией, и он работает с полным содержимым вашего списка.Чтобы получить предсказуемый результат, не сохраняйте stream во временной переменной, так как это приведет к вводящим в заблуждение результатам.Лучший код:

String[] arr = strList.stream().filter(str -> str.startsWith("J")).toArray();
0 голосов
/ 22 декабря 2018

@ Комментарий Хади Дж, но он должен отвечать согласно правилам.

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

0 голосов
/ 22 декабря 2018

Это потому, что поток так и не был оценен.Вы никогда не вызывали « Терминальная операция » в этом потоке, чтобы он выполнялся, поскольку они lazy .

Посмотрите на модификацию вашего кода и вывод,Фильтрация фактически происходит при вызове оператора терминала.

 public static void main(String []args){
         List<String> strList = new ArrayList<>();
    strList.add("Java");
    strList.add("Python");
    strList.add("Php");

    Stream<String> strStream = strList.stream();

    Stream<String> jFilter = strStream.filter(str -> {
        System.out.println("Filtering" + str);
        return str.startsWith("J");
        });

 System.out.println("After Stream creation");
    strList.add("JavaScript"); // element added after filter creation
    strList.add("JQuery"); // element added after filter creation

    System.out.println(Arrays.toString(jFilter.toArray()));

     }

Вывод:

After Stream creation
FilteringJava
FilteringPython
FilteringPhp
FilteringJavaScript
FilteringJQuery
[Java, JavaScript, JQuery]
0 голосов
/ 22 декабря 2018

Как объяснено в официальной документации по адресу, https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html, потоки не имеют хранилища и поэтому больше похожи на итераторы, чем на коллекции, и оцениваются лениво.

Таким образом, ничего не происходит в отношениипоток, пока вы не вызовете терминальную операцию toArray ()

0 голосов
/ 22 декабря 2018

Пока оператор

System.out.println(Arrays.toString(jFilter.toArray()));

не выполняется, поток ничего не делает.Для прохождения потока и выполнения промежуточных операций (в данном случае filter) требуется операция терминала (toArray в этом примере).

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

int maxSize = strList.size();
Stream<String> jFilter = strStream.limit(maxSize)
                                  .filter(str -> str.startsWith("J"));

, где limit(maxSize) не позволит проходить по конвейеру более чем начальным элементам.

0 голосов
/ 22 декабря 2018

Короткий ответ

Вы предполагаете, что после этого момента:

Stream<String> jFilter = strStream.filter(str -> str.startsWith("J"));

, что возвращается новый поток элементов, начинающихся с "J", т.е. только Java.Однако это не случай;

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

Фактическое выполнение конвейера потоканачинается при вызове toArray(), и поскольку список был изменен до начала операции терминала toArray(), результатом будет [Java, JavaScript, JQuery].

Longer Answer

здесь часть документация , в которой упоминается это:

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

 List<String> l = new ArrayList(Arrays.asList("one", "two"));
 Stream<String> sl = l.stream();
 l.add("three");
 String s = sl.collect(joining(" "));  

Сначала создается список, состоящий из двух строк: «one»;и два".Затем создается поток из этого списка.Далее список модифицируется добавлением третьей строки: «три».Наконец элементы потока собираются и объединяются.Поскольку список был изменен до начала операции сбора терминала, результатом будет строка «один два три».Все потоки, возвращаемые из коллекций JDK и большинства других классов JDK, ведут себя хорошо;

...