As a matter of Factory — Part 1 (Mutable)

I’ve read a few blogs, articles and tweets over the past year about an upcoming feature in Java 9. Some of the articles have been titled “Immutable Collections in Java 9”. There will be new factory methods on the Java collections interfaces (List, Set, Map) that will create “compact, unmodifiable collection instances”. This feature is described in JEP 269 — http://openjdk.java.net/jeps/269. This feature will be a welcome addition for many Java developers, especially in places like unit tests, where creating small collections can be a frequent and potentially cumbersome activity.

But… we are not getting “contractually” immutable collections in Java 9 and JEP 269 does not claim to provide them. If we were getting “contractually” immutable collections in Java 9, I would expect to see new interfaces named ImmutableCollection, ImmutableSet, ImmutableList and ImmutableMap. Those interfaces could then have the “of” factory methods to produce compile time safe immutable collection instances. ImmutableCollection interfaces should not have methods like add/addAll, remove/removeAll on them because these methods are unsafe and can only throw UnsupportedOperationExceptions.

If you are looking for contractually immutable collections for Java, there are a few open source libraries available today that provide them. Eclipse Collections provides both mutable and immutable collections, and has collection factories for both of them. In part 1 of this blog, I will compare the mutable factory methods available in Eclipse Collections with the new factory methods that will be added in Java 9. In part 2, I will explore the factories available for immutable collections.

There have always been static factory methods on all of the mutable container implementations in Eclipse Collections. These methods are still available, so you can write the following to create mutable Lists, Sets and Maps using Eclipse Collections.

MutableList<String> list = 
FastList.newListWith("1", "2", "3");
MutableSet<String> set = 
UnifiedSet.newSetWith("1", "2", "3");
MutableMap<Integer, String> map = 
UnifiedMap.newWithKeysValues(1, "1", 2, "2", 3, "3");

For developers new to the library, these methods can be hard to discover because the developer first needs to know the name of the appropriate implementation classes .

When immutable collections were introduced in the library, we had to make a decision how to create them. We wanted a consistent approach for creating both mutable and immutable collections that would follow a consistent convention that would be easy for developers to learn. Our solution was to create dedicated collection factory classes. We decided the convention we would use with our factory classes would be to match the container types we had with corresponding types that ended in “s”. So List(s), Set(s), Map(s), etc. We aim for good symmetry here so we can meet developer expectations. Here’s the equivalent code to create mutable collection instances using the factory classes.

MutableList<String> list = 
Lists.mutable.with("1", "2", "3");
MutableSet<String> set = 
Sets.mutable.with("1", "2", "3");
MutableMap<Integer, String> map = 
Maps.mutable.with(1, "1", 2, "2");

If you want to create empty mutable collections, simply use the empty() method.

MutableList<String> list = 
Lists.mutable.empty();
MutableSet<String> set = 
Sets.mutable.empty();
MutableMap<Integer, String> map = 
Maps.mutable.empty();

If you would like a more succinct option for creating collections, you can use the Iterables class with static imports. Then you can write the following.

MutableList<String> list = 
mList("1", "2", "3");
MutableSet<String> set = 
mSet("1", "2", "3");
MutableMap<Integer, String> map = 
mMap(1, "1", 2, "2", 3, "3");

The “m” methods are short for “mutable” and there are also “i” methods which are short for “immutable”.

All of the mutable interfaces in Eclipse Collections extend the corresponding mutable interfaces in the JDK. So MutableList is a java.util.List, MutableSet is a java.util.Set, etc.

Yellow — JDK, Blue — Eclipse Collections

The mutable factories will return the most specific type (e.g. MutableList), but you can choose to use a more abstract type (e.g. java.util.List).

List<String> list = 
Lists.mutable.with("1", "2", "3");
Set<String> set = 
Sets.mutable.with("1", "2", "3");
Map<Integer, String> map = 
Maps.mutable.with(1, "1", 2, "2", 3, "3");

The factory methods being added in Java 9 will look as follows.

List<String> list = 
List.of("1", "2", "3");
Set<String> set = 
Set.of("1", "2", "3");
Map<Integer, String> map = 
Map.of(1, "1", 2, "2", 3, "3");

The difference is that the above methods return unmodifiable instances which have mutable interfaces.

If you want to use the java.util.Collection interfaces, and want the instances returned to be unmodifiable like the Java 9 factory methods directly above, you can use asUnmodifiable() method available on all mutable collections in Eclipse Collections.

List<String> list = 
Lists.mutable.with("1", "2", "3").asUnmodifiable();
Set<String> set = 
Sets.mutable.with("1", "2", "3").asUnmodifiable();
Map<Integer, String> map = 
Maps.mutable.with(1, "1", 2, "2", 3, "3").asUnmodifiable();

If you want them to be synchronized, you can use asSynchronized().

List<String> list = 
Lists.mutable.with("1", "2", "3").asSynchronized();
Set<String> set = 
Sets.mutable.with("1", "2", "3").asSynchronized();
Map<Integer, String> map = 
Maps.mutable.with(1, "1", 2, "2", 3, "3").asSynchronized();

The following shows all of the factories for mutable object collections that are available in Eclipse Collections.

MutableList<T> list = Lists.mutable.empty();
MutableSet<T> set = Sets.mutable.empty();
MutableSortedSet<T> sortedSet = SortedSets.mutable.empty();
MutableMap<K, V> map = Maps.mutable.empty();
MutableSortedMap<K, V> sortedMap = SortedMaps.mutable.empty();
MutableStack<T> stack = Stacks.mutable.empty();
MutableBag<T> bag = Bags.mutable.empty();
MutableSortedBag<T> sortedBag = SortedBags.mutable.empty();
MutableBiMap<K, V> biMap = BiMaps.mutable.empty();
MutableListMultimap<K, V> multimap = Multimaps.mutable.list.empty();
MutableSetMultimap<K, V> multimap = Multimaps.mutable.set.empty();
MutableBagMultimap<K, V> multimap = Multimaps.mutable.bag.empty();

Eclipse Collections also supports containers for all eight of the Java primitive types as well. In order to provide good symmetry with their object counterparts, there are factory classes for all of the mutable primitive container types as well.

MutableIntList list = 
IntLists.mutable.empty();
MutableIntSet set =
IntSets.mutable.empty();
MutableIntBag bag =
IntBags.mutable.empty();
MutableIntStack stack =
IntStacks.mutable.empty();
// supports all combinations for all 8 primitives
MutableIntIntMap map =
IntIntMaps.mutable.empty();
MutableIntObjectMap<V> map =
IntObjectMaps.mutable.empty();
MutableObjectIntMap<K> map =
ObjectIntMaps.mutable.empty();

There are factories for all primitive types for all container types. The mutable primitive containers also have unmodifiable and synchronized versions as well, to provide good symmetry with their object counterparts.

MutableIntList list = 
IntLists.mutable.with(1, 2, 3).asUnmodifiable();
MutableIntList list = 
IntLists.mutable.with(1, 2, 3).asSynchronized();

The collection factories in Eclipse Collections work with all versions of Java all the way back to version 5. If you want to use the collection factories with Java versions prior to Java 8, you will need to use Eclipse Collections 7.x. Eclipse Collections 8.x is only compatible with Java 8+. Both Eclipse Collections 7.x and 8.x work with Java 8.

With of or of with

All of the examples that I have shown so far that use “with”, can also be written using “of” as well. In a naming battle between “with” and “of”, there were two winners. So you can write the following if you prefer of instead of with. FWIW, the “with” approach is more consistent with Smalltalk style.

List<String> list = 
Lists.mutable.of("1", "2", "3");
Set<String> set = 
Sets.mutable.of("1", "2", "3");
Map<Integer, String> map = 
Maps.mutable.of(1, "1", 2, "2", 3, "3");

I think this is a good place to stop for now. In part 2 of this blog series, I will give an overview of the immutable factories that are available for Eclipse Collections.