Java8: использование IntStream в качестве параметра поставщика для настраиваемого коллектора - PullRequest
0 голосов
/ 03 мая 2018

Для потоковых объектов нельзя ли использовать метод collect для записи пользовательского объекта Collector, который будет собираться в другой потоковый объект? Я только недавно узнал о потоках и пытаюсь получить от них как можно больше опыта, и столкнулся с этой проблемой.

Мой код:

    // method to return how many unique letters are used in
    // the Strings in the given stream
    public int uniqueLetters(Stream<String> stream){
        // transfer Strings to an uppercase char stream
        IntStream allLets = stream.collect(IntStream::empty, 
                          (s1, s2) -> { String toAdd = s2.toUpperCase();
                                        IntStream cs = toAdd.chars();
                                        IntStream.concat(s1, cs); //exception thrown on this line
                                       }, IntStream::concat); 
        // use distinct on char stream and count
        return (int) allLets.distinct().count();
    }

Этот код компилируется, и метод collect работает нормально для первой строки в потоке, но во второй раз, когда он достигает IntStream.concat (s1, cs), я получаю следующее исключение:

java.lang.IllegalStateException: поток уже был обработан или закрыт

Я интерпретирую это так, как только моя первая строка преобразуется в IntStream, а метод collect переходит к следующей строке, мой первый IntStream закрывается. Это верно? Почему это происходит?

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

1 Ответ

0 голосов
/ 03 мая 2018

Позвонив по номеру concat, вы не мутируете s1. concat возвращает объединенный поток. Во-первых, потоки не изменяемы.

Это означает, что Stream.empty, с которого вы начали, никогда не менялся, и он используется в методе concat снова и снова. Однако вы не можете использовать поток в concat дважды. Это считается «воздействовать на поток». Поэтому, когда вы попытаетесь сделать это во второй раз, вы получите недопустимое исключение из состояния, как описано:

Поток должен работать (вызывая промежуточный или терминальный потоковая операция) только один раз. Это исключает, например, «раздвоение» потоки, где один и тот же источник питает два или более конвейеров, или множественные обходы одного и того же потока. Реализация потока может бросить IllegalStateException, если он обнаруживает, что поток повторно. Однако, поскольку некоторые потоковые операции могут возвращать свои приемник, а не новый объект потока, может оказаться невозможным обнаружить повторное использование во всех случаях.

Чтобы решить проблему с поиском уникальных персонажей, вам не нужно собирать данные в потоки. Просто сделайте это:

public static int uniqueLetters(Stream<String> stream){
    return (int)stream.flatMapToInt(String::chars).distinct().count();
}

flatMap это, вероятно, то, что вы пытались сделать, собирая в потоки. Теперь вы знаете, что это называется flatMap.

...