Stream.toList() and other converter methods I’ve wanted since Java 2

Donald Raab
Javarevisited
Published in
6 min readJan 23, 2021

Better late than never?

A perfect dinner setting after 7 years of planning — Grounds for Sculpture, Hamilton, NJ

Self-service only

In 2004, I was an architect coding in Java at a large financial services firm. Java was missing most of the collection productivity features I had in Smalltalk, so I decided I would “Just do it” and started building the first utility classes in what would eventually become an open-source Java library called Eclipse Collections. I blogged about this a year ago.

Serving a full menu dinner every day for 40 years

Smalltalk has always had converter methods for its collection types. A converter method allows you to convert one type to another via an intention revealing method name. In Smalltalk, the converter methods all started with the prefix “as”. I created a mind map capturing the converter methods available on the Smalltalk Collection API. Smalltalk has had these methods for the past 40 years.

Smalltalk converter methods

These methods have been proven useful for the past 40 years by Smalltalk developers. I used all of them at one time or other when I was developing professionally in Smalltalk in the 1990's.

Smalltalk -> Java equivalents

  • asArray -> toArray
  • asDictionary -> Collectors.toMap
  • asOrderedCollection -> Collectors.toList
  • asSet -> Collectors.toSet

Java uses the prefix “to” for converter methods in the Collectors class and for toArray on Collection and Stream. The other methods available in Smalltalk have no equivalent in Collectors today.

So why did Smalltalk add the symmetric converter methods for List, Bag,Set, Map, IdentitySet, OrderedMap, SortedList 40 years ago? I believe it’s because the benefit far outweighs the cost of each of the methods.

Planning the perfect dinner for 7 years

When Java 16 is released in March, seven years will have passed since Java 8 was released with lambdas and Streams. The new Stream.toList() method in Java 16 will be somewhat disappointing without its family and friends (toSet, toMap, toCollection).

Doing a quick search on GitHub, I found the following occurrences of Collectors.toList(), Collectors.toSet(), Collectors.toMap(), and Collectors.toCollection().

  • Collectors.toList() -> 1,363,648
  • Collectors.toSet() -> 283,944
  • Collectors.toMap() -> 169,753
  • Collectors.toCollection() -> 61,831
  • Collectors.counting() -> 14,765 (equivalent of toBag)
  • Collectors.toUnmodifiableList() -> 4,712
  • Collectors.toUnmodifiableSet() -> 2,064
  • Collectors.toUnmodifiableMap() -> 1,386

Clearly, toList() is the most commonly used method. This is not surprising. But there are over half a million other instances of converter method usages. This is only searching for projects in GitHub.

Most enterprise applications are not in GitHub. I would expect at least one to two orders of magnitude more usages for each of these if we could scan all private Java code repos in the world.

After toList is added to Stream in JDK 16, how many months or years will it be before we see toSet, toMap and toCollection?

Hobson’s Dinner Menu

When we get Stream.toList() in Java 16, it will be a choice of one meal for dinner every night. We can use any converter method we want on Stream as long as it is toList. This is a Hobson’s choice.

If we need another converter method, we can use collect and Collectors for other collection types. This will lead to an unfortunate asymmetry in the Stream API usage. The method toList will be very fluent, whereas using Collectors for other types is less so.

Mutable or Immutable or Unmodifiable

Naming is important. Naming something well is difficult. List is a mutable interface. There are “unmodifiable” implementations of the interface, which makes the List interface “conditionally” mutable. This is similar to the “synchronized” implementations of List which provide “conditional thread safety”.

With anything that is conditional, it is up to the developer to understand and handle any potential conditionality. I would expect a method named toList, to return a mutable type.

The name does not indicate that an unmodifiable or immutable implementation is returned. The caller has to look past the name and the return type, and look at the specification in Javadoc or explore the implementation code directly to understand the behavior supported by the result returned.

Since an unmodifiable implementation is returned, the method would have been more clear if it was named toUnmodifiableList. This name would have provided good symmetry with Collectors.toUnmodifiableList(). Stream.toList is actually more similar to Collectors.toUnmodifiabList. If toList actually returned a mutable List, it would have provided good symmetry with Collectors.toList().

Stream.toCollection = All you can eat buffet

It would be very beneficial to add the method toCollection to Stream. Collectors.toCollection takes a Supplier that returns a subtype of Collection. The actual implementation type returned is up to the developer, and all implementations must be mutable.

<T, R extends Collection<T>> R toCollection(Supplier<R> supplier)

This allows Stream working nicely with all types of Collection in the universe. It would also be consistent with the same method on Collectors. Its implementation can be covered simply via a default method. If brevity was desired, I would shorten the name to simply “to”, since the Supplier clarifies the return type already.

<T, R extends Collection<T>> R to(Supplier<R> supplier)

Multiple Choice Dinner Menu

If the symmetry of converter methods is important to you, you do have a choice. You can always use Eclipse Collections which has many converter methods on both object and primitive Iterables. The converter methods available on RichIterable are as follows:

RichIterable Converter Methods

RichIterable is the parent interface of most of the object type containers in Eclipse Collections.

If you would like to learn about the many converter methods that are available in Eclipse Collections today, there is a blog I wrote at the end of 2020, with a related code kata.

Summary

The method Stream.toList() which will arrive in JDK 16 with an unmodifiable return type will probably cause some confusion. I teach Java to both new and experienced developers.

The name toList is not intention revealing by itself. Naming the method toUnmodifiableList would have made it consistent with the same method on Collectors.

Serving well-done hamburgers with only one bun after a 7-year wait, when it says medium rare hamburgers on the menu may leave some developers unsatisfied. Having no fish or vegetarian meal option may also be disappointing (toSet and toMap).

In JDK 17, adding the method toCollection to Stream would cover many more use cases than the new Stream.toList method. There will be no confusion in the naming, as it will be completely consistent with the method named toCollection on Collectors.

This would give developers a nearly unlimited menu. Since we’re getting toList it anyway, we really should add this method to keep developer’s appetites satisfied.

Eclipse Collections already has many converter methods for object, primitive, serial, parallel, eager, lazy, mutable, and immutable APIs. Having Stream.toCollection would allow fluent conversions to the extensive number of collection types to convert to in Eclipse Collections from Streams.

This would be an excellent addition. I plan to start advocating for it to be included in JDK 17. I would prefer the simpler and shorter name of to, but would settle for toCollection to be consistent with Collectors.toCollection. Consistency and clarity are more important than brevity.

I am a Project Lead and Committer for the Eclipse Collections OSS project at the Eclipse Foundation. Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.

--

--

Javarevisited
Javarevisited

Published in Javarevisited

A humble place to learn Java and Programming better.

Donald Raab
Donald Raab

Written by Donald Raab

Java Champion. Creator of the Eclipse Collections OSS Java library (https://github.com/eclipse/eclipse-collections). Inspired by Smalltalk. Opinions are my own.