Home > Enterprise >  How do Java stream methods operate?
How do Java stream methods operate?

Time:03-25

I just about understand Java streams. One can create a stream by calling .stream() on a Collection, and then you can operate on that stream with methods as described by the docs.

What I don't understand is where the process actually occurs. What I mean by that is sure I can create a stream of Person objects with an age attribute and sort it like so:

Person john = new Person(30);
Person anne = new Person(25);
Person bill = new Person(52);

ArrayList<Person> people = new ArrayList<Person>();
people.add(john);
people.add(anne);
people.add(bill);

ArrayList<Person> sortedPeople = people
.stream()
.sorted(Comparators.comparing(person -> person.getAge()))
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

But in the docs and the source code I have found, functional interfaces like the one containing the sorted method has no sorting code and just describes the lambda function passed to it. Have I massively misunderstood something here?

CodePudding user response:

When you called ArrayList#stream(), you got back what looks like a Stream but is actually an object of some unspecified concrete class that implements the Stream interface.

This is part of the beauty of OOP: We don’t know what unspecified concrete class is actually used under the hood, nor do we care. All we care about is that the mystery concrete class (or a superclass) fulfills the contract promised by the interface, the Stream interface in this case.

functional interfaces like the one containing the sorted method has no sorting code

That unspecified concrete class has the sorting mechanism implemented, implementing the sorted method as required by the Stream interface.

just describes the lambda function passed

That lambda you pass is actual code that will be executed as part of the sorting mechanism.


If you are still curious about that mystery concrete class, you could browse the open source code for ArrayList#stream found on the OpenJDK project. Start here. You’ll likely need to comb through the source code for super-classes as well.

And, you could interrogate the Stream object for its concrete class. Every object in Java is a subclass of Object class, so all have a method getClass. Using the Class class, you can get a name for the class.

persons.stream().getClass().getCanonicalName()

When I run this with my copy of Java 17.0.2 implementation named Zulu by Azul Systems, I get:

java.util.stream.ReferencePipeline.Head

I do not find any such class in the Java 17 API. So that class must be internally defined within whatever implementation of Java I am currently using. Other implementations may differ, as might past and future versions.

Nowadays, most implementations of Java are based largely on the codebase at OpenJDK. This includes Zulu. So looking at OpenJDK, I do find that nested Head class in the source code for ReferencePipeline.


By the way, your code could be simplified to something like this.

record Person( String name , int age ) { }

List < Person > persons =
        new ArrayList <>(
                List.of(
                        new Person( "Alice" , 52 ) ,
                        new Person( "Bob" , 23 ) ,
                        new Person( "Carol" , 39 )
                )
        );

List < Person > sorted =
        persons
                .stream()
                .sorted( Comparator.comparing( Person :: age ) )
                .toList();

persons = [Person[name=Alice, age=52], Person[name=Bob, age=23], Person[name=Carol, age=39]]

sorted = [Person[name=Bob, age=23], Person[name=Carol, age=39], Person[name=Alice, age=52]]

  • Related