Подтвердить что ты не робот

Есть ли способ повторно использовать поток?

Я изучаю новые функции Java 8 и, экспериментируя с потоками (java.util.stream.Stream) и сборщиками, я понял, что поток нельзя использовать дважды.

Есть ли способ его использовать?

4b9b3361

Ответ 1

Если вы хотите получить эффект повторного использования потока, вы можете обернуть выражение потока в поставщике и вызывать myStreamSupplier.get() всякий раз, когда вы хотите новый. Например:

Supplier<Stream<String>> sup = () -> someList.stream();
List<String> nonEmptyStrings = sup.get().filter(s -> !s.isEmpty()).collect(Collectors.toList());
Set<String> uniqueStrings = sup.get().collect(Collectors.toSet());

Ответ 2

Из документации:

Поток должен работать (вызывать операцию промежуточного или терминального потока) только один раз.

Реализация потока может вызывать исключение IllegalStateException, если оно обнаруживает, что поток повторно используется.

Таким образом, ответ отрицательный, потоки не предназначены для повторного использования.

Ответ 3

Как говорили другие, "нет, вы не можете".

Но полезно запомнить удобный summaryStatistics() для многих основных операций:

Итак, вместо:

List<Person> personList = getPersons();

personList.stream().mapToInt(p -> p.getAge()).average().getAsDouble();
personList.stream().mapToInt(p -> p.getAge()).min().getAsInt();
personList.stream().mapToInt(p -> p.getAge()).max().getAsInt();

Вы можете:

// Can also be DoubleSummaryStatistics from mapToDouble()

IntSummaryStatistics stats = personList.stream()
                                       .mapToInt(p-> p.getAge())
                                       .summaryStatistics();

stats.getAverage();
stats.getMin();
stats.getMax();

Ответ 4

Вся идея Stream состоит в том, что он разом. Это позволяет создавать не возвращаемые источники (например, чтение строк из сетевого подключения) без промежуточного хранения. Однако, если вы хотите повторно использовать контент Stream, вы можете выгрузить его в промежуточную коллекцию, чтобы получить "твердую копию":

Stream<MyType> stream = // get the stream from somewhere
List<MyType> list = stream.collect(Collectors.toList()); // materialize the stream contents
list.stream().doSomething // create a new stream from the list
list.stream().doSomethingElse // create one more stream from the list

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

Ответ 5

Если подумать, эта воля "повторного использования" потока - это просто воля к достижению желаемого результата с помощью хорошей встроенной операции. Итак, в основном, о чем мы здесь говорим, что мы можем сделать, чтобы продолжить обработку после того, как мы написали терминальную операцию?

1) если ваша терминальная операция возвращает коллекцию, проблема сразу решается, поскольку каждая коллекция может быть возвращена в поток (JDK 8).

List<Integer> l=Arrays.asList(5,10,14);
        l.stream()
            .filter(nth-> nth>5)
            .collect(Collectors.toList())
            .stream()
            .filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));

2) если ваши терминальные операции возвращают необязательный, с расширениями JDK 9 до необязательного класса, вы можете превратить необязательный результат в поток и получить желаемую красивую встроенную операцию:

List<Integer> l=Arrays.asList(5,10,14);
        l.stream()
            .filter(nth-> nth>5)
            .findAny()
            .stream()
            .filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));

3) если ваша терминальная операция возвращает что-то еще, я действительно сомневаюсь, что вы должны рассмотреть поток для обработки такого результата:

List<Integer> l=Arrays.asList(5,10,14);
        boolean allEven=l.stream()
            .filter(nth-> nth>5)
            .allMatch(nth-> nth%2==0);
        if(allEven){
            ...
        }

Ответ 6

Как уже отмечали другие, сам объект потока не может быть повторно использован.

Но один из способов получить эффект повторного использования потока - извлечь код создания потока в функцию.

Вы можете сделать это, создав метод или объект функции, который содержит код создания потока. Затем вы можете использовать его несколько раз.

Пример:

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

    // The normal way to use a stream:
    List<String> result1 = list.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());

    // The stream operation can be extracted to a local function to
    // be reused on multiple sources:
    Function<List<Integer>, List<String>> listOperation = l -> l.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());

    List<String> result2 = listOperation.apply(list);
    List<String> result3 = listOperation.apply(Arrays.asList(1, 2, 3));

    // Or the stream operation can be extracted to a static method,
    // if it doesn't refer to any local variables:
    List<String> result4 = streamMethod(list);

    // The stream operation can also have Stream as argument and return value,
    // so that it can be used as a component of a longer stream pipeline:
    Function<Stream<Integer>, Stream<String>> streamOperation = s -> s
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i);

    List<String> result5 = streamOperation.apply(list.stream().map(i -> i * 2))
        .filter(s -> s.length() < 7)
        .sorted()
        .collect(toCollection(LinkedList::new));
}

public static List<String> streamMethod(List<Integer> l) {
    return l.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());
}

Если, с другой стороны, у вас уже есть объект потока, который вы хотите повторить несколько раз, вы должны сохранить содержимое потока в некотором объекте коллекции.

Затем вы можете получить несколько потоков с одинаковым содержимым из коллекции.

Пример:

public void test(Stream<Integer> stream) {
    // Create a copy of the stream elements
    List<Integer> streamCopy = stream.collect(toList());

    // Use the copy to get multiple streams
    List<Integer> result1 = streamCopy.stream() ...
    List<Integer> result2 = streamCopy.stream() ...
}

Ответ 7

Функциональная библиотека Java предоставляет свои собственные потоки, которые делают то, что вы просите, то есть они запоминаются и ленивы. Вы можете использовать его методы преобразования для преобразования между объектами Java SDK и объектами FJ, например, Java8.JavaStream_Stream(stream) вернет повторно используемый поток FJ с учетом потока JDK 8.