It’s time for some Java Reindeer games.

Image for post
Image for post
A Festival of lights in New York

This blog is going to be mostly code. I am going to demonstrate some Eclipse Collections and Java Stream APIs, with a holiday twist. I’ll start by implementing a Reindeer Enum in Java with some help from Eclipse Collections.

public enum Reindeer
{
Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner, Blitzen,
Rudolph;
public static ImmutableList<Reindeer> all()
{
return theMostFamousReindeerOfAll()
.newWithAll(theOtherReindeer()
.flatCollect(Reindeer::toList));
}
private static ImmutableList<Twin<Reindeer>> theOtherReindeer()
{
return Lists.immutable.with(
Dasher.and(Dancer),
Prancer.and(Vixen),
Comet.and(Cupid),
Donner.and(Blitzen));
}
private static
ImmutableList<Reindeer> theMostFamousReindeerOfAll()
{
return Lists.immutable.with(Reindeer.Rudolph);
}
private static
ImmutableList<Reindeer> toList(Twin<Reindeer> twin)
{
return Lists.immutable.with(twin.getOne(), twin.getTwo());
}
public Twin<Reindeer> and(Reindeer other)
{
return Tuples.twin(this, other);
}
public int nameLength()
{
return this.name().length();
}
public Character firstLetterOfName()
{
return Character.valueOf(this.name().charAt(0));
}
}

First, I create an ImmutableList of Reindeer with Rudolph at the front in the method named all. I create an ImmutableList of pairs of all of the other reindeer, based on their usual order in the popular song. I use the type Twin, which is a Pair that has the same type for both items. Finally, I used the method flatCollect to flatten all of the pairs of Reindeer into a single list which is appended to the ImmutableList with Rudolph using the newWithAll method.

Now I will implement some tests for the Reindeer name games.

/**
* Create a comma separated String of the Reindeer names.
*/
@Test
public void reindeerNameGame1()
{
String expectedNames = "Rudolph, " +
"Dasher, Dancer, " +
"Prancer, Vixen, " +
"Comet, Cupid, " +
"Donner, Blitzen";
Assert.assertEquals(
expectedNames,
Reindeer.all().makeString(", "));
Assert.assertEquals(
expectedNames,
String.join(", ",
Reindeer.all()
.asLazy()
.collect(Reindeer::name)));
Assert.assertEquals(
expectedNames,
Reindeer.all()
.stream()
.map(Reindeer::name)
.collect(Collectors.joining(", ")));
}

In this test, I show three different ways a comma separated String of the reindeer names can be created. First, I use makeString from Eclipse Collections. The method makeString does not require the object to be a CharSequence. It uses the toString implementation of the Object. Next, I use String.join which was added in Java 8. This method takes an Iterable of CharSequence, which I create using a LazyIterable with collect. Finally, I use a stream with the ImmutableList returned from all and then map each Reindeer to its name, and collect them all into a String using Collectors.joining. All three approaches have the exact same result.

/**
* Count the Reindeer names based on their size.
*/
@Test
public void reindeerNameGame2()
{
IntBag nameCounts =
Reindeer.all()
.asLazy()
.collectInt(Reindeer::nameLength).toBag();

IntBag nameCountsFromIntStream =
IntBags.mutable.withAll(
Reindeer.all()
.stream()
.mapToInt(Reindeer::nameLength));

Assert.assertEquals(nameCounts, nameCountsFromIntStream);
Assert.assertEquals(3, nameCounts.occurrencesOf(5));
Assert.assertEquals(3, nameCounts.occurrencesOf(6));
Assert.assertEquals(3, nameCounts.occurrencesOf(7));

Bag<Integer> nameCountsBy =
Reindeer.all().countBy(Reindeer::nameLength);

Assert.assertEquals(3, nameCountsBy.occurrencesOf(5));
Assert.assertEquals(3, nameCountsBy.occurrencesOf(6));
Assert.assertEquals(3, nameCountsBy.occurrencesOf(7));

Map<Integer, Long> streamNameCounts =
Reindeer.all()
.stream()
.collect(Collectors.groupingBy(
Reindeer::nameLength,
Collectors.counting()));

Assert.assertEquals(new Long(3), streamNameCounts.get(5));
Assert.assertEquals(new Long(3), streamNameCounts.get(6));
Assert.assertEquals(new Long(3), streamNameCounts.get(7));
}

In this test, I show four different ways to count the names of the Reindeer by their length. First, I create an IntBag, by collecting all of the name lengths for the Reindeer using collectInt and then converting the result toBag. In the second case, I create an IntStream from the Reindeer by mapping their name lengths to an int using mapToInt. I use the withAll method to create an IntBag from an IntStream. This method became available in Eclipse Collections 9.0 (Look at Item #3 for a comparison to previous versions). The third solution is the simplest. I use the method countBy which is available on types which extend RichIterable in Eclipse Collections. Finally, I use Stream with Collectors.groupingBy and Collectors.counting which results in a Map of Integer to Long.

/**
* Group Reindeer by the first letter of their names.
*/
@Test
public void reindeerNameGame3()
{
Multimap<Character, Reindeer> multimap =
Reindeer.all().groupBy(Reindeer::firstLetterOfName);
Map<Character, List<Reindeer>> mapOfLists =
Reindeer.all()
.stream()
.collect(Collectors.groupingBy(
Reindeer::firstLetterOfName));
Assert.assertEquals("Dasher, Dancer, Donner",
multimap.get('D').makeString(", "));
Assert.assertEquals("Comet, Cupid",
multimap.get('C').makeString(", "));
Assert.assertEquals(multimap.get('D'), mapOfLists.get('D'));
Assert.assertEquals(multimap.get('C'), mapOfLists.get('C'));
}

In this test, I illustrate two ways to group Reindeer by the first letter of their last name. First, I use the groupBy method available directly on ImmutableList, which returns a Multimap. In the second example, I use the stream method on ImmutableList and collect method on Stream with the groupingBy Collector on the Collectors utility class.

I hope you enjoyed joining in these reindeer games. I explored a bunch of Eclipse Collections and Streams APIs in these three examples that may be useful in your own Java code.

Have a Happy Holiday and Happy New Year!

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

Written by

Java Champion. Creator of the Eclipse Collections OSS Java library (http://www.eclipse.org/collections/). Inspired by Smalltalk. Opinions are my own.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store