On Sun, 7 Nov 2021 07:51:06 GMT, Michael Bien <d...@openjdk.java.net> wrote:
>>> wouldn't this make streams no longer lazy if the collection is empty? >>> >>> ```java >>> List<String> list = new ArrayList<>(); >>> Stream<String> stream = list.stream(); >>> >>> list.addAll(List.of("one", "two", "three")); >>> >>> stream.forEach(System.out::println); // prints one two three >>> ``` >> >> I did not consider this case, thank you for bringing it up. I have always >> found this behaviour a bit strange and have never used it "in the real >> world". It is also not consistent between collections. Here is an example >> with four collections: ArrayList, CopyOnWriteArrayList, >> ConcurrentSkipListSet and ArrayBlockingQueue: >> >> >> import java.util.ArrayList; >> import java.util.Arrays; >> import java.util.Collection; >> import java.util.List; >> import java.util.Objects; >> import java.util.concurrent.ArrayBlockingQueue; >> import java.util.concurrent.ConcurrentSkipListSet; >> import java.util.concurrent.CopyOnWriteArrayList; >> import java.util.function.Supplier; >> import java.util.stream.IntStream; >> >> public class LazyStreamDemo { >> public static void main(String... args) { >> List<Supplier<Collection<String>>> suppliers = >> List.of(ArrayList::new, // fast-fail >> CopyOnWriteArrayList::new, // snapshot >> ConcurrentSkipListSet::new, // weakly-consistent >> () -> new ArrayBlockingQueue<>(10) // >> weakly-consistent >> ); >> for (Supplier<Collection<String>> supplier : suppliers) { >> Collection<String> c = supplier.get(); >> System.out.println(c.getClass()); >> IntStream stream = c.stream() >> .sorted() >> .filter(Objects::nonNull) >> .mapToInt(String::length) >> .sorted(); >> >> c.addAll(List.of("one", "two", "three", "four", "five")); >> System.out.println("stream = " + >> Arrays.toString(stream.toArray())); >> } >> } >> } >> >> >> The output is: >> >> >> class java.util.ArrayList >> stream = [3, 3, 4, 4, 5] >> class java.util.concurrent.CopyOnWriteArrayList >> stream = [] >> class java.util.concurrent.ConcurrentSkipListSet >> stream = [] >> class java.util.concurrent.ArrayBlockingQueue >> stream = [3, 3, 4, 4, 5] >> >> >> At least with the EmptyStream we would have consistent output of always [] > > @kabutz I agree that i wouldn't consider it clean code to use a stream like i > put into the example. I only brought it up because it might break existing > code, since i think streams are expected to be lazy. Interesting to see that > they are in fact not lazy in all situations - i put that into my notes. Edit: actually I think the implementation of `Collection::stream` could simply be changed to: default Stream<E> stream() { var spliterator = spliterator(); if(spliterator.hasCharacteristics(Spliterator.IMMUTABLE | Spliterator.CONCURRENT) && isEmpty()) { return Stream.empty(); } return StreamSupport.stream(spliterator, false); } Note that the spliterators of methods such as `Collections::emptyList`, `List::of`, `List::copyOf`, `Set::of`, ... currently don't have the `IMMUTABLE` characteristic though, so they should be updated. --- The Javadoc for [java.util.stream](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/stream/package-summary.html) is clear though: > Traversal of the pipeline source does not begin until the terminal operation > of the pipeline is executed. and further on says: > Spliterators for mutable data sources have an additional challenge; timing of > binding to the data, since the data could change between the time the > spliterator is created and the time the stream pipeline is executed. Ideally, > a spliterator for a stream would report a characteristic of IMMUTABLE or > CONCURRENT; if not it should be late-binding. which explains why the collections in `java.util.concurrent` (whose spliterators have one of those characteristics) need not be late-binding. ------------- PR: https://git.openjdk.java.net/jdk/pull/6275