Когда используется Java 8 Stream, который считается потребляемым?
Я понял, что Java 8 Stream
считается потребляемым после выполнения терминальной операции, например forEach()
или count()
.
Однако тестовый пример multipleFilters_separate
ниже вызывает IllegalStateException
, хотя filter
является ленивой промежуточной операцией, просто называемой как два оператора. И тем не менее, я могу связать две операции фильтра с одним оператором и работать.
@Test(expected=IllegalStateException.class)
public void multipleFilters_separate() {
Stream<Double> ints = Stream.of(1.1, 2.2, 3.3);
ints.filter(d -> d > 1.3);
ints.filter(d -> d > 2.3).forEach(System.out::println);
}
@Test
public void multipleFilters_piped() {
Stream<Double> ints = Stream.of(1.1, 2.2, 3.3);
ints.filter(d -> d > 1.3)
.filter(d -> d > 2.3)
.forEach(System.out::println);
}
Из этого я предполагаю, что Stream
считается потребляемым после первого оператора, который использует его, независимо от того, вызывает ли этот оператор операцию терминала или нет. Правильно ли это звучит?
Ответы
Ответ 1
A Stream
считается потребляемым после выполнения операции терминала. Однако даже несколько промежуточных операций не должны выполняться для одного и того же экземпляра Stream
, как указано в Stream
javadoc:
Поток должен быть включен (вызов операции промежуточного или конечного потока) только один раз. Это исключает, например, "разветвленные" потоки, где один и тот же источник передает два или более конвейера или несколько обходов одного и того же потока. Реализация потока может вызывать исключение IllegalStateException, если он обнаруживает, что поток повторно используется. Однако, поскольку некоторые потоковые операции могут возвращать их приемник, а не новый объект потока, может быть невозможно обнаружить повторное использование во всех случаев.
В случае операций промежуточного потока вы должны вызвать следующую операцию на Stream
, возвращенную предыдущей операцией:
public void multipleFilters_separate() {
Stream<Double> ints = Stream.of(1.1, 2.2, 3.3);
ints = ints.filter(d -> d > 1.3);
ints.filter(d -> d > 2.3).forEach(System.out::println);
}
Ответ 2
В соответствии с Stream
Javadoc:
Поток должен работать (вызывать операцию промежуточного или терминального потока) только один раз. Это исключает, например, "разветвленные" потоки, где один и тот же источник передает два или более конвейера или несколько обходов одного и того же потока. Реализация потока может бросать IllegalStateException
, если он обнаруживает, что поток повторно используется. Однако, поскольку некоторые потоковые операции могут возвращать их приемник, а не новый объект потока, возможно, не удастся обнаружить повторное использование во всех случаях.
В вашем случае вызов filter
сам обнаруживает попытку превратить ваш поток в два разных потока. Вместо того, чтобы ждать и вызывать проблемы после того, как операция терминала добавлена, она выполняет упреждающий удар, чтобы очистить его от любой трассировки стека, где вы точно получаете свою проблему.