toList() vs collect(Collectors.toList())

I had some extra time this week so went through a bunch of Sonar findings. One was interesting – in Java 17 you can use .toList() instead of .collect(Collectors.toList()) on a stream.

[Yes, I know this was introduced in Java 16. I live in a world where only LTS releases matter]

Cool. I can fix a lot of these without thinking. It’s a search and replace on the project level after all. I then ran the JUnit regression tests and got failures. That was puzzling to me because I’ve been using .toList() in code I write for a good while without incident.

After looking into it, I found the problem. .toList() guarantees the returned List is immutable. However, Collectors.toList() makes no promises about immutability. The result might be immutable. Or you can change it freely. Surprise?

That’s according to the spec. On the JDK I’m using (and Jenkins is using), Collectors.toList() was returning an ArrayList. So people were treating the returned List as mutable and it was working. I added a bunch of “let’s make this explicitly mutable” and then I was able to commit.

Here’s an example that illustrates the diference

import java.util.*;
import java.util.stream.*;

public class PlayTest {

	public static void main(String[] args) {

		var list = List.of("a", "b", "c");
		var collectorReturned = collector(list);
		var toListReturned = toList(list);
		
		System.out.println(collectorReturned.getClass());  // ArrayList (but doesn't have to be)
		System.out.println(toListReturned.getClass());  // class java.util.ImmutableCollections$ListN
		
		collectorReturned.add("x");
		System.out.println(collectorReturned);  // [bb, cc, x]
		toListReturned.add("x");  // throws UnsupportedOperationException

	}

	private static List<String> toList(List<String> list) {
		return list.stream()
				.filter(s -> ! s.equals("a"))
				.map(s -> s + s)
				.toList();
	}

	private static List<String> collector(List<String> list) {
		return list.stream()
				.filter(s -> ! s.equals("a"))
				.map(s -> s + s)
				.collect(Collectors.toList());
				
	}

Collectors.toList() also makes no promises about serializablity or thread safety but I wasn’t expecting it to.

2 thoughts on “toList() vs collect(Collectors.toList())

  1. Hi,
    Sorry for asking here, I searched in your books and tried to find your email, but did not find that. I also sent you in X but did not get any reply.
    Can you please confirm that Java constant must be static final, I men only final is not what we call constant right?
    AFAIK, constant should be available without any instance of class is needed + its value shouldnt be changed + follow java code convention and named SNAKE_CASE then it is called constant.
    The only final field is called only final, it is not called constant and should be named camelCase.
    Am I correct?
    Thanks in advance and sorry again for asking here.

Leave a Reply

Your email address will not be published. Required fields are marked *