0

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?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • 1
    I think you should point out one specific interface and describe what exactly you find lacking. Your question as it is rather general and doesn't really tell me how to answer your question. – markspace Mar 24 '22 at 16:57
  • 2
    There is at least some documentation "how streams work" on the `Stream` interface itself: https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html – markspace Mar 24 '22 at 16:58
  • 2
    Besides the documentation on classes, there is also package documentation. You should always try to read the relevant package docs if you're confused, they often contain important information: https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/package-summary.html – markspace Mar 24 '22 at 17:00
  • 1
    Related: [What does it mean to program to an interface?](https://stackoverflow.com/questions/1413543/what-does-it-mean-to-program-to-a-interface) – Mark Rotteveel Mar 24 '22 at 17:26

1 Answers1

7

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.toString() = [Person[name=Alice, age=52], Person[name=Bob, age=23], Person[name=Carol, age=39]]

sorted.toString() = [Person[name=Bob, age=23], Person[name=Carol, age=39], Person[name=Alice, age=52]]

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • I would like to remind the OP that the reason why this works is because the data item being used for sorting is "comparable". In this case, there is a natural order to numbers as well as strings. So, if the OP wishes to sort by name it will work as well. However, this is only true because `String` implements the `Comparable` interface. I say this to say that, if the OP needs to compare `Person` objects, the class will need to specify what that natural order is with regards to `Person` objects. – hfontanez Mar 24 '22 at 21:52
  • @hfontanez Actually, why *does* this code work with a field member of primitive type `int`? Does auto-boxing kick-in at some point here? – Basil Bourque Mar 25 '22 at 04:47
  • I am not quite sure, but my guess is that there is some other type of manipulation done for primitive types. I have to look that up. – hfontanez Mar 25 '22 at 04:56
  • After a bit or research and some thinking, I think that is the case. I think it works because of auto-boxing. The `Comparator` interface dates back to Java 1.2 and there was no way to use this class to compare primitives. Back then, you had to manually convert the primitive to their wrapper class and do the comparison that way, or do it mathematically. – hfontanez Mar 25 '22 at 05:10
  • 1
    @hfontanez I have a similar recollection, but vaguely. Perhaps this would make a good Question in its own right. So, [I posted one](https://stackoverflow.com/q/71612495/642706). – Basil Bourque Mar 25 '22 at 05:21