Страницы

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

пятница, 31 января 2020 г.

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

#java #java_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;
}

    


Ответы

Ответ 1



Как верно отметил @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); }

Ответ 2



Stream нельзя использовать несколько раз. Поток пропускается один раз. По этому, если цикл повторяется больше одного раза то код работает не корректно. Во втором примере, каждый раз создаётся новый stream .

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

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