toBe(), or not toBe()?
Converter methods are more than a convenience in a programming language. They are a means to discovering additional collection types available to developers. When the number of available collection types is large, this becomes even more important to have good discoverability. Smalltalk has mostly mutable collection types. Java and Eclipse Collections both have mutable and immutable implementations. Only Eclipse Collections has mutable and immutable types as separate interfaces. Eclipse Collections also has primitive collections, so converter methods help provide helpful symmetry and discoverability between Object and primitive collection types.
toSmalltalk
In Smalltalk, converter methods are prefixed with as. The Collection abstract class has eleven converter methods — asArray, asBag, asByteArray, asCharacterSet, asDictionary, asIdentitySet, asMultilineString, asOrderedCollection, asOrderedDictionary, asSet, asSortedCollection.
This is the code from the above image inlined.
|ordered sorted set bag|
ordered := OrderedCollection with: 'Apple' with: 'Pear' with: 'Banana' with: 'Apple'.
sorted := ordered asSortedCollection: #yourself descending.
set := ordered asSet.
bag := sorted asBag.Transcript show: ordered printString; cr.
Transcript show: sorted printString; cr.
Transcript show: set printString; cr.
Transcript show: bag printString; cr.
This is the output:
an OrderedCollection('Apple' 'Pear' 'Banana' 'Apple')
a SortedCollection('Pear' 'Banana' 'Apple' 'Apple')
a Set('Pear' 'Banana' 'Apple')
a Bag('Pear' 'Banana' 'Apple' 'Apple')IIRC, most of the Collection types available via converter methods in Smalltalk are mutable.
toJava
In Java, converter methods are prefixed with to. The Collection interface has two converter methods — toString and toArray. The Stream interface has three converter methods —toString, toArray, toList. The Stream interface has a collect method which takes a Collector as a parameter. The Collectors utility class has nine unique to methods (some are overloaded) — toCollection, toList, toSet, toMap, toConcurrentMap, toUnmodifiableList, toUnmodifiableSet, toUnmodifiableMap, toConcurrentMap.
To convert a List to a “sorted” List, a Set, and a Bag , we can use the following. There is no SortedList or Bag type in Java, but we’ll find an equivalent.
@Test
public void converterMethodsInJava()
{
List<String> ordered =
List.of("Apple", "Pear", "Banana", "Apple");
List<String> sorted =
ordered.stream()
.sorted(Comparator.reverseOrder())
.toList();
Set<String> set =
ordered.stream()
.collect(Collectors.toSet());
Map<String, Long> bag =
sorted.stream()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()));assertEquals(
List.of("Pear", "Banana", "Apple", "Apple"),
sorted);
assertEquals(
Set.of("Pear", "Banana", "Apple"),
set);
assertEquals(
Map.of("Pear", 1L, "Banana", 1L, "Apple", 2L),
bag);
}
Most of the converter methods in Java are three steps away from Collection. I do not think it is likely we will see any more converter methods on Collection or Stream.
Note, in the code example above it is not easy to distinguish between mutable and immutable collection implementations. You have to read the Javadoc or code to understand the return types of different methods.
IntelliJ also recommends not using the converter method in the case of Collectors.toSet().
IntelliJ recommends writing it as follows and this can be accomplished by hitting Alt-Enter and choosing the recommended action above.
Set<String> set = new HashSet<>(ordered);Using this approach is more concise and is probably more performant (measure, don’t guess), but it introduces more asymmetry and draws implementation details (java.util.HashSet class) into our example.
toEclipseCollections
In Eclipse Collections, like Java, we use the to prefix for converter methods that have a linear time cost. The Eclipse Collections RichIterable interface has twenty six unique converter methods (some are overloaded). The converter methods can be found using IntelliJ Structure view in a category named “Converting”.
To convert a List to a “sorted” List, a Set, and a Bag , we can use the following. There is no SortedList in Eclipse Collections, but we’ll find an equivalent. We will use mutable collections in these examples, and we can tell they are mutable based on the type names.
@Test
public void converterMethodsInEclipseCollections()
{
MutableList<String> ordered =
Lists.mutable.of("Apple", "Pear", "Banana", "Apple");
MutableList<String> sorted =
ordered.toSortedList(Comparator.reverseOrder());
MutableSet<String> set =
ordered.toSet();
MutableBag<String> bag =
sorted.toBag();assertEquals(
List.of("Pear", "Banana", "Apple", "Apple"),
sorted);
assertEquals(
Set.of("Pear", "Banana", "Apple"),
set);
assertEquals(
Bags.mutable.withOccurrences("Apple", 2, "Pear", 1, "Banana", 1),
bag);
}
If we want to use immutable collections in Eclipse Collections, the code would look like this.
@Test
public void immutableConverterMethodsInEclipseCollections()
{
ImmutableList<String> ordered =
Lists.immutable.of("Apple", "Pear", "Banana", "Apple");
ImmutableList<String> sorted =
ordered.toImmutableSortedList(Comparator.reverseOrder());
ImmutableSet<String> set =
ordered.toImmutableSet();
ImmutableBag<String> bag =
sorted.toImmutableBag();assertEquals(
List.of("Pear", "Banana", "Apple", "Apple"),
sorted);
assertEquals(
Set.of("Pear", "Banana", "Apple"),
set);
assertEquals(
Bags.mutable.withOccurrences("Apple", 2, "Pear", 1, "Banana", 1),
bag);
}
I did not have to change the assertions in the example. The equals and hashCode contract for mutable and immutable types of the same container type is the same.
Takeaways
Java is a great language. Java’s standard Collection library is useable but does not have great symmetry or convenience. Eclipse Collections brings back and extends the conveniences Smalltalk had thirty years ago into Java today, and adds symmetry for mutable and immutable converter methods.
Think about the code you are writing today, and what it will be like maintaining it for the next 5, 10, 20 or 30 years. Writing code that communicates well helpful in reducing the cost of understanding and maintenance. Well written code will also help new developers learn how things work, without having to memorize a lot of asymmetric alternatives to converting between collection types.
If you want to learn more about converter methods in Eclipse Collections, I have blogged about them previously, and they are also covered in Chapter 4 of the book “Eclipse Collections Categorically.” Here is a table of most of the mutable and immutable converter methods described in Chapter 4.
and I also covered some converter methods in our “Refactoring to Eclipse Collections” talk at dev2next which I blogged about here.
If you don’t think this applies to you because you’ve moved to Kotlin, Python, Ruby, or some other language, take a look at the Kotlin Collections to methods. Once Immutable Collections become part of the Kotlin standard library, I will take an educated guess how this will grow.
Thanks for reading!
I am the creator of and committer for the Eclipse Collections OSS project, which is managed at the Eclipse Foundation. Eclipse Collections is open for contributions. I am the author of the book, Eclipse Collections Categorically: Level up your programming game.