How, Why and When to use Stream.toList() method in Java?

Java 16 introduced a new method toList() to Stream interface.

How is this going to help Java Developers?

Let’s see it through an example.

Let us take the use case where you want to iterate through a stream of objects, filter it based on certain criteria and save the results in a list.

Traditionally you iterated through the list of objects (there were no concept of stream prior to Java 8) , manually filtered the objects through conditional checks and added the result to a new list.

Example:

Let us say we need to filter a list of patients whose age is greater than 45.

Here is the Patient class and the code to filter the patients

package jvaa16features;

public class Patient {

	
	String name;
	
	int age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Patient(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Patient [name=" + name + ", age=" + age + "]";
	}
	
	
}


		List<Patient> patientList = new ArrayList<>();

		patientList.add(new Patient("Mr A", 56));
		patientList.add(new Patient("Mrs B", 37));
		patientList.add(new Patient("Mr C", 78));

		// traditional way to filter data - filter patients above age 45

		List<Patient> filteredPatients = new ArrayList<>();
		for (Patient p : patientList) {

			if (p.getAge() > 45) {

				filteredPatients.add(p);

			}
		}
		
		System.out.println("Traditional way");
		System.out.println(filteredPatients);
		
		

It prints the below output:

Traditional way
[Patient [name=Mr A, age=56], Patient [name=Mr C, age=78]]

Since the introduction of streams in Java 8 , you can reduce the lines of code using the below code to filter patients :

		
		List<Patient> filteredPatientUsingCollectors = patientList.stream().filter(p -> p.getAge() > 45).collect(Collectors.toList());

		System.out.println(filteredPatientUsingCollectors);

Notice that you convert the list to a steam , filter it based on age using lamda function and then collect the result using collect method and Collectors.toList() method.

The above prints the same output as before.

Starting Java 16, you can use toList() method directly on the filtered stream instead of collect(Collectors.toList()) method.

Here is an example:

	List<Patient> filteredPatientsUsingStreamToList = patientList.stream().filter(p -> p.getAge() > 45).toList();
		System.out.println(filteredPatientsUsingStreamToList);

The above produces the same initial output as well.

The later code appear much simpler and elegant.

Apart from this , is there any other difference between Stream.toList() method and Stream.collect(Collectors.toList()) method?

Yes ,

The list you get out of Stream.toList() method is immutable . You cannot change the list once it is prepared.

On the other hand , the list you get out of Stream.collect() method is mutable. You can change any object in the list after it is prepared.

Here is an example.

I am trying to change the second object in the filtered list of objects prepared using both the methods.

Here is the code:

	

		List<Patient> patientList = new ArrayList<>();

		patientList.add(new Patient("Mr A", 56));
		patientList.add(new Patient("Mrs B", 37));
		patientList.add(new Patient("Mr C", 78));

System.out.println("Collectors to List");
		
		List<Patient> filteredPatientUsingCollectors = patientList.stream().filter(p -> p.getAge() > 45).collect(Collectors.toList());
		Patient patientnew = new Patient("Mr D",89);
		filteredPatientUsingCollectors.set(1, patientnew);
		System.out.println(filteredPatientUsingCollectors);
		
		
		
		System.out.println("Stream ToList");
		
		List<Patient> filteredPatientsUsingStreamToList = patientList.stream().filter(p -> p.getAge() > 45).toList();
		
		Patient patientnew1  = new Patient("Mr E",89);
		filteredPatientsUsingStreamToList.set(1, patientnew1);
		System.out.println(filteredPatientsUsingStreamToList);

Here is the output:

Collectors to List
[Patient [name=Mr A, age=56], Patient [name=Mr D, age=89]]
Stream ToList
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
	at java.base/java.util.ImmutableCollections$AbstractImmutableList.set(ImmutableCollections.java:260)
	at jvaa16features.StreamToList.main(StreamToList.java:60)

As you see the first filtered list of objects gets modified without any issue.

But the second filtered list of objects when modified throw UnsupportedOperationException.

So use Stream.toList() if you want an immutable filtered list and Stream.collect() if you want a mutable list.

Here is the entire code:

package jvaa16features;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class StreamToList {

	public static void main(String a[]) {


		List<Patient> patientList = new ArrayList<>();

		patientList.add(new Patient("Mr A", 56));
		patientList.add(new Patient("Mrs B", 37));
		patientList.add(new Patient("Mr C", 78));

		// traditional way to filter data - filter patients above age 45

		List<Patient> filteredPatients = new ArrayList<>();
		for (Patient p : patientList) {

			if (p.getAge() > 45) {

				filteredPatients.add(p);

			}
		}
		
		System.out.println("Traditional way");
		System.out.println(filteredPatients);
		
		
		
		//using Collectors.toList()
		System.out.println("Collectors to List");
		
		List<Patient> filteredPatientUsingCollectors = patientList.stream().filter(p -> p.getAge() > 45).collect(Collectors.toList());
		Patient patientne = new Patient("Mr D",89);
		filteredPatientUsingCollectors.set(1, patientne);
		System.out.println(filteredPatientUsingCollectors);
		
		
		
		System.out.println("Stream ToList");
		
		List<Patient> filteredPatientsUsingStreamToList = patientList.stream().filter(p -> p.getAge() > 45).toList();
		
		Patient patientnew  = new Patient("Mr E",89);
		filteredPatientsUsingStreamToList.set(1, patientnew);
		System.out.println(filteredPatientsUsingStreamToList);
		
		

	}
}

Here is the github url to the code:

https://github.com/vijaysrj/java16features/tree/master/src/jvaa16features

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s