As a matter of Factory — Part 3 (Method Chaining)
By: Donald Raab
Tags: Eclipse Collections, Open Source, Software Development, Java, Programming

Equivalent Methods from JDK interfaces

with -> add

without -> remove

withAll -> addAll

withoutAll -> removeAll

withKeyValue -> put

withoutKey -> removeKey

Fluently building Maps

The with method on MutableListFactory can construct a MutableList with a variable number of items, but the same is not true for MutableMapFactory. The with method on MutableMapFactory is overloaded five times.

<K, V> MutableMap<K, V> with(); <K, V> MutableMap<K, V> with(K key, V value); <K, V> MutableMap<K, V> with(K key1, V value1, K key2, V value2); <K, V> MutableMap<K, V> with(K key1, V value1, K key2, V value2, K key3, V value3); <K, V> MutableMap<K, V> with(K key1, V value1, K key2, V value2, K key3, V value3, K key4, V value4);

You can only use the with method method to create a MutableMap with up to four keys and values.

MutableMap<Integer, String> map =

Maps.mutable.with(

1, "One",

2, "Two",

3, "Three",

4, "Four");

What do you do if you need more than four keys and values?

There is a method named withKeyValue on MutableMap which can be used to fluently add an unlimited number of key and value pairs by leveraging method chaining.

Fluently building a MutableMap using withKeyValue

MutableMap<Integer, String> numbers =

Maps.mutable.<Integer, String>empty()

.withKeyValue(0, "Zero")

.withKeyValue(1, "One")

.withKeyValue(2, "Two")

.withKeyValue(3, "Three")

.withKeyValue(4, "Four")

.withKeyValue(5, "Five")

.withKeyValue(6, "Six")

.withKeyValue(7, "Seven")

.withKeyValue(8, "Eight")

.withKeyValue(9, "Nine")

.withKeyValue(10, "Ten"); Assert.assertEquals("Zero", numbers.get(0));

Assert.assertEquals("One", numbers.get(1));

Assert.assertEquals("Ten", numbers.get(10));

The withKeyValue method

public MutableMap<K, V> withKeyValue(K key, V value)

{

this.put(key, value);

return this;

}

Fluently building an ImmutableMap

ImmutableMap<Integer, String> numbers =

Maps.mutable.<Integer, String>empty()

.withKeyValue(0, "Zero")

.withKeyValue(1, "One")

.withKeyValue(2, "Two")

.withKeyValue(3, "Three")

.withKeyValue(4, "Four")

.withKeyValue(5, "Five")

.withKeyValue(6, "Six")

.withKeyValue(7, "Seven")

.withKeyValue(8, "Eight")

.withKeyValue(9, "Nine")

.withKeyValue(10, "Ten")

.toImmutable(); Assert.assertEquals("Zero", numbers.get(0));

Assert.assertEquals("One", numbers.get(1));

Assert.assertEquals("Ten", numbers.get(10));

Fluently building an Immutable object / primitive Map

ObjectIntMap<String> numbers =

ObjectIntMaps.mutable.<String>empty()

.withKeyValue("Zero", 0)

.withKeyValue("One", 1)

.withKeyValue("Two", 2)

.withKeyValue("Three", 3)

.withKeyValue("Four", 4)

.withKeyValue("Five", 5)

.withKeyValue("Six", 6)

.withKeyValue("Seven", 7)

.withKeyValue("Eight", 8)

.withKeyValue("Nine", 9)

.withKeyValue("Ten", 10)

.toImmutable(); Assert.assertEquals(0, numbers.get("Zero"));

Assert.assertEquals(1, numbers.get("One"));

Assert.assertEquals(10, numbers.get("Ten"));

Fluently building an Immutable primitive / object Map

IntObjectMap<String> numbers =

IntObjectMaps.mutable.<String>empty()

.withKeyValue(0, "Zero")

.withKeyValue(1, "One")

.withKeyValue(2, "Two")

.withKeyValue(3, "Three")

.withKeyValue(4, "Four")

.withKeyValue(5, "Five")

.withKeyValue(6, "Six")

.withKeyValue(7, "Seven")

.withKeyValue(8, "Eight")

.withKeyValue(9, "Nine")

.withKeyValue(10, "Ten")

.toImmutable(); Assert.assertEquals("Zero", numbers.get(0));

Assert.assertEquals("One", numbers.get(1));

Assert.assertEquals("Ten", numbers.get(10));

Fluently building an Immutable primitive / primitive Map

IntCharMap numbers = IntCharMaps.mutable.empty()

.withKeyValue(0, '0')

.withKeyValue(1, '1')

.withKeyValue(2, '2')

.withKeyValue(3, '3')

.withKeyValue(4, '4')

.withKeyValue(5, '5')

.withKeyValue(6, '6')

.withKeyValue(7, '7')

.withKeyValue(8, '8')

.withKeyValue(9, '9')

.toImmutable(); Assert.assertEquals('0', numbers.get(0));

Assert.assertEquals('1', numbers.get(1));

Assert.assertEquals('9', numbers.get(9));

Fluently building Lists, Sets and Bags

Fluently building a Map using method chaining is often more useful than fluently building a List, Set, or Bag. Still the functionality exists if and when you need it.

Fluently building a List

ImmutableList<Integer> numbers =

Lists.mutable.with(1)

.with(2)

.with(3)

.with(4)

.withAll(Interval.fromTo(5, 10))

.toImmutable();

Assert.assertEquals(Interval.oneTo(10), numbers);

Notice that I used two methods to fluently build the MutableList. The method with takes a single argument and the method withAll takes an Iterable.

The with method

public MutableList<T> with(T element)

{

this.add(element);

return this;

}

The withAll method

public MutableList<T> withAll(Iterable<? extends T> elements)

{

this.addAllIterable(elements);

return this;

}

Fluently building a Set

ImmutableSet<Integer> numbers =

Sets.mutable.with(1)

.with(2)

.with(3)

.with(4)

.withAll(Interval.fromTo(5, 10))

.toImmutable();

Assert.assertEquals(Interval.oneTo(10).toSet(), numbers);

Fluently building a Bag

ImmutableBag<Integer> numbers =

Bags.mutable.with(1)

.with(2)

.with(3)

.with(4)

.withAll(Interval.fromTo(5, 10))

.toImmutable();

Assert.assertEquals(Interval.oneTo(10).toBag(), numbers);

Fluently building primitive Lists, Sets and Bags

ImmutableIntList list =

IntLists.mutable.with(1)

.with(2)

.with(3)

.with(4)

.withAll(IntInterval.fromTo(5, 10))

.toImmutable();

Assert.assertEquals(IntInterval.oneTo(10), list); ImmutableIntSet set =

IntSets.mutable.with(1)

.with(2)

.with(3)

.with(4)

.withAll(IntInterval.fromTo(5, 10))

.toImmutable();

Assert.assertEquals(IntInterval.oneTo(10).toSet(), set); ImmutableIntBag bag =

IntBags.mutable.with(1)

.with(2)

.with(3)

.with(4)

.withAll(IntInterval.fromTo(5, 10))

.toImmutable();

Assert.assertEquals(IntInterval.oneTo(10).toBag(), bag);

Using withAll in Collectors2

The withAll method is very useful for building Collectors which return collections. The withAll method is used repeatedly as a method reference in the Collectors2 class of Eclipse Collections.

Collectors2.toList()

public static <T> Collector<T, ?, MutableList<T>> toList()

{

return Collector.of(

Lists.mutable::empty,

MutableList::add,

MutableList::withAll,

EMPTY_CHARACTERISTICS);

}

Collectors2.toImmutableList()

public static <T> Collector<T, ?, ImmutableList<T>> toImmutableList()

{

return Collector.<T, MutableList<T>, ImmutableList<T>>of(

Lists.mutable::empty,

MutableList::add,

MutableList::withAll,

MutableList::toImmutable,

EMPTY_CHARACTERISTICS);

}

Collectors2.countBy(Function)

public static <T, K> Collector<T, ?, MutableBag<K>> countBy(Function<? super T, ? extends K> function)

{

return Collector.of(

Bags.mutable::empty,

(bag, each) -> bag.with(function.valueOf(each)),

MutableBag::withAll,

EMPTY_CHARACTERISTICS);

}

With or Without

We like to provide good symmetry in Eclipse Collections. If you can fluently add items to a collection, then it would make sense to be be able to remove items from a collection fluently.

The without method on UnifiedSet

public UnifiedSet<T> without(T element)

{

this.remove(element);

return this;

}

Here’s an example combining some of the fluent aspects of the Eclipse Collections factories.

ImmutableSet<String> strings =

Sets.mutable.with("or", "without", "you")

.with("or")

.without("you")

.toImmutable();

Assert.assertEquals(Sets.mutable.with("or", "without"), strings);

Final Thoughts

Hopefully you found the information and examples in this blog useful. This blog was inspired by a recent pull request submitted to Eclipse Collections. There are a lot of hidden gems inside of Eclipse Collections. I like to write about them when it becomes clear they are not so easily discovered.

Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.