Страницы

Поиск по вопросам

среда, 3 апреля 2019 г.

Как использовать Stream повторно?

Сегодня сидел разбирался с нововведениями Java 8. Когда попробовал на практике, сразу столкнулся с ошибкой. Наверное, я чего-то не знаю. Объясните, пожалуйста, что не так.
Вот такой код:
private boolean test( String line ) { String[] words = line.split(" "); Stream wordsStream = Arrays.asList(words).stream(); for( int i=0; i < controlWords.length; i++ ) { if( !wordsStream.anyMatch( Predicate.isEqual(controlWords[i]) ) ) { return false; } } return true; }
Если массив controlWords имеет размер > 1, то всё время получаю ошибку
java.lang.IllegalStateException: stream has already been operated upon or closed
Если же метод чуть-чуть видоизменить, то проблема исчезнет:
private boolean test( String line ) { String[] words = line.split(" "); //Stream wordsStream = Arrays.asList(words).stream(); for( int i=0; i < controlWords.length; i++ ) { if( !Arrays.asList(words).stream().anyMatch( Predicate.isEqual(controlWords[i]) ) ) { return false; } } return true; }


Ответ

Как верно отметил @kofemann, Stream — одноразовая штука. В этом плане он похож на Iterator. Чтобы использовать его несколько раз, нужно создавать новый Stream. Если вы по какой-то причине хотите создание Stream вынести наружу (например, чтобы вам Stream передавали параметром), но при этом вам он нужен несколько раз, рекомендуется использовать Supplier
private boolean test(String line) { String[] words = line.split(" "); Supplier> wordsStream = () -> Arrays.asList(words).stream(); for (int i = 0; i < controlWords.length; i++) { if (!wordsStream.get().anyMatch(Predicate.isEqual(controlWords[i]))) { return false; } } return true; }
Ещё обратите внимание, что есть метод Arrays.stream(), поэтому вам необязательно заворачивать сперва слова в список:
Supplier> wordsStream = () -> Arrays.stream(words);
Наконец, заметьте, что ваша задача решается гораздо легче старыми способами без Stream API:
private boolean test(String line) { List words = Arrays.asList(line.split(" ")); for (int i = 0; i < controlWords.length; i++) { if (!words.contains(controlWords[i])) { return false; } } return true; }
Однако можно повернуть в другую сторону и уже сам цикл for свернуть в Stream из controlWords
private boolean test(String line) { List words = Arrays.asList(line.split(" ")); return Arrays.stream(controlWords).allMatch(controlWord -> words.contains(controlWord)); }
Тут можно заменить лямбду на ссылку:
private boolean test(String line) { List words = Arrays.asList(line.split(" ")); return Arrays.stream(controlWords).allMatch(words::contains); }
И даже заинлайнить words (после инлайнинга он всё равно один раз вычисляется!)
private boolean test(String line) { return Arrays.stream(controlWords).allMatch(Arrays.asList(line.split(" "))::contains); }

Комментариев нет:

Отправить комментарий