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.

The 8 Nights of Java – Night 2

Continuing The 8 Nights of Java series, tonight we focus on Java 1.2. While Java 1.0 and 1.1 were about establishing a strong base and creating a set of classes that directly interfaced with operating system components, such as threads, Java 1.2 was focused on building on top of that base with convenient and reusable classes. For example, you could create your own dynamic array using the built-in primitive arrays, but do you really want to? Java 1.2 answered this by showing that Java was more than just a language, it was a useful set of reusable tools written by software developers, for software developers.

Jump to: [Night 1 | Night 2 | Night 3 | Night 4 | Night 5 | Night 6 | Night 7 | Night 8]

Java 1.2 Notable Features
Sun released Java 1.2 (codename Playground) on December 8, 1998. This version included:

  • Collections framework
  • Swing
  • Java Applet browser plug-in

From Jeanne:

Hard to believe the Collections framework has been with us for so long. It works. There’s lots of collections and opportunities for extension. And I’m thankful we don’t have a Collections2 or Collections3 framework. Something this core is useful to last unchanged. There have been Collection implementations added over time, but that makes sense because it is a framework. Even in Java 8, Oracle added static and default methods so they could leave the interfaces backward compatible.

As for the others. Well. I’ve written exactly one Swing program professionally. And I only did it because Console.readPassword() wasn’t available yet at that time. Unfortunately it is still with us. The wonders of “legacy code.” The Java browser plugin served its use. More recently, its contributed to feelings of Java being insecure though. It also contributed to some confusion between Java and JavaScript. I’ll never forget the day the help desk told me to reinstall Java to “solve” a JavaScript browser problem. Needless to say, I declined. And the problem was not related to Java after all. Gasp!

Fun fact: my very first Java program for school used Java 1.2.

From Scott:

I have to agree with Jeanne on this one. Although the Collections framework was a little difficult to use until generics were added in Java 5.0, they were from the start very powerful. I remember studying linked lists, hash maps, tree sets, and sorting in school. While all software developers should know these concepts and be able to implement them… in production software engineering it’s far better to go with a feature-rich implementation that has been written and tested by hundreds of developers than your own piecemeal solution (aka a “square wheel”).

As for Swing, I’ll be honest and say there was never a time I enjoyed programming in Swing. Even “back in the day” the Swing UI already looked quite dated. It was very difficult to write Swing code that looked good without relying on a number of often proprietary plugins, which required licenses to use. I remember registering for courses online (pilot program at the time, previous students had to do so in person) and a Swing app popped up within the browser, then promptly crashed 4-5 times in a row. I don’t know if the Swing apps I had the misfortune to use were just poorly written, poorly designed, or the underlying technology was just flawed. The one thing I do know is that I have never looked at a Swing app (either one that I’ve written or used) and thought it looked “beautiful” or was “easy to use”.

The limitation of Swing, as well as other Java UI languages, still holds true today in professional software development. While many people use Java for back-end systems, most use some flavor of Javascript (Angular, Node.js, Ajax) or mobile design tool (xib, storyboard, view, layout) for the majority of their front-end work. Even JavaFX, which was later billed as the “greatest Java UI language since Swing” was never able to gain much ground, in part because so many developers carried over a dislike of Swing. I do wonder how Java would be different today, if Swing had been easier to use and looked nicer.