-1

When we get Stream from a List of Collections, what actually happens under the hood? I found in many blogs saying that Stream doesn't store any data. If it's true, consider the snippet of code:

List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);

Stream<Integer> stream = list.stream();
list = null;
stream.forEach(i -> System.out.print(i));

As we can see, elements are accessible to stream even if List (source) is null. How is this possible?

2
  • 4
    I don't know how streams work internally, but setting that reference to null doesn't really show anything. The stream probably holds its own reference to the data, meaning setting list to null just made the data unavailable in that scope. Commented Aug 6, 2018 at 17:52
  • this question does not make any sense, setting a reference to null works the same in almost every language, and if you think that will have side effects on other objects of which you have the reference to (in this case stream) you should go back study the basics Commented Nov 8, 2024 at 12:03

2 Answers 2

1

The Stream API has no idea what is inside the List until the lambda expression is performed over the first and remaining elements.

stream.forEach(i -> System.out.print(i));

The code above is nothing else than a shortcut for:

stream.forEach(new Consumer<String>() {
    @Override
    public void accept(String t) {
        System.out.println(t);
    }
});

The lambda expression is just an implemented interface, the functional interface which has only and only one method. This special interface is annotated with @FunctionalInterface and could be expressed as a lambda expression.

The point is, the method accept(String t) doesn't execute before it's actually called - the same happens for the lambda. Consider the following:

Consumer<String> consumer = i -> System.out.print(i);

// At this point the method `System.out.print(i)` remains not executed.
// Whatever expensive operation inside any lambda remains unexecuted 
// until its only method is called

stream.forEach(consumer);   // Now it encounters all the elements

For this reason, the Stream has no idea about the elements because it leaves the entire execution to the passed lambda expressions.


By the way setting list = null doesn't affect anything since the Stream<Integer> has already been created. The source is stored as Spliterator<T> in AbstractPipeline (line 123).

Sign up to request clarification or add additional context in comments.

Comments

1

This has little to do with Streams per-se, a very much simplified example:

 static class Holder {
    private final List<Integer> list;

    public Holder(List<Integer> list) {
        this.list = list;
    }

    public void show() {
        list.forEach(System.out::println);
    }
}

 Holder h = new Holder(list);
 list = null;
 h.show();

This simply re-assigns a reference, nothing more.

3 Comments

Thumbs up for not getting lost in streams, but pointing out that it is just a question of reference vs value and that the list still exists.
In above example you are assigning the reference to the member variable(list) and nullifying the parameter(list), which is perfectly fine & works as expected. But in my example, I'm trying to nullify the source which the stream is pointing to. In some tutorials they said that, stream is meant for processing elements but doesn't store any. If it's true, how I am able reference the elements even if the source that stream points to is marked as NULL.
@AnoopDeshpande you are nullifying the reference, not the source. and when you call stream() there will be a new reference pointing to the same source, internally that reference will be used

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.