Javarevisited
Published in

Javarevisited

Map vs. Bag

Discover a better way to count things in Java.

Photo by Crissy Jarvis on Unsplash

Using Map to count the beads on an abacus

Here’s a simple problem to solve using a Map. How can we count the number of beads tallied on an abacus by color? First, I will define an enum for Color.

Enum for different Color beads on an Abacus

Next, I will create a HashMap<Color, Integer> and use the merge method to add and subtract values for a specific colors. I will leave one color bead (Color.RED) out from the counting. Then I will assert the result of querying the Map for the counts of the colors using Map.get.

Using a Map as an abacus

I use the same merge operation for adding and subtracting. I use negative Integer values to subtract. I use the method reference Integer::sum for the BiFunction parameter the merge method takes to determine how to merge values. Note, that even though I am using primitive literal int values here, they are auto-boxed as Integer objects in the Map., and the Integer::sum call will result in more unboxing and boxing of Integer objects.

In the final assertion in the test, the call to get with the Color.RED instance returns null. I would have to explicitly set the value to 0 for it to come back as 0 when calling get. One way to address the null return problem with get, is to use getOrDefault instead. This will allow you to return a default value in the case that the key does not exist in the Map.

Using a Map as an abacus, handling null cases using getOrDefault

The merge and getOrDefault methods were both added in Java 8, and are useful methods for developers to understand on Map.

Using a primitive Map instead of a Map

In Eclipse Collections, there is support for primitive Maps. There is an ObjectIntMap type and corresponding implementation that can be used to count the beads. I will use a MutableObjectIntMap so I can mutate the map and create the implementation using the ObjectIntMaps factory class.

Using a MutableObjectIntMap as an abacus

A MutableObjectIntMap has an addToValue method that can be used to put a new value in the map, or add to an existing value in a map. The method getIfAbsent can be used to look up the values. The 0 value is an int, not an Integer object, and none of the calls to addToValue result in any boxing.

Note: Eclipse Collections HashBag class leverages a MutableObjectIntMap.

Using Bag to count the beads on an abacus

So how would I solve this problem using a MutableBag from Eclipse Collections? A MutableBag is a Collection, not a Map. It has add and remove methods which I can use to add single items. MutableBag also has addOccurrences and removeOccurrences methods which allow adding a specific number of items at once.

Using a Bag as an abacus

The individually named methods on Bag are more intention revealing than the single method named merge for this use case. Notice that in the case of Color.RED, the Bag will return 0 from the call to occurrencesOf. Bag does not suffer from the null problem that Map.get does. If a value does not exist in the Bag, the occurrencesOf method always returns 0.

More Information

There are a few more blogs which provide more details on the Bag type and supporting methods like countBy in Eclipse Collections. The following blog was written by Nikhil Nanivadekar. It describes many of the implementation details of the Bag type in Eclipse Collections, that can result in both memory and performance benefits.

I also wrote a separate blog describing the countBy method in Eclipse Collections which provides Bag as its return type.

I hope you enjoyed reading this blog and learned something useful about Map and Bag types in the process!

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.

Other Java Articles you may like

--

--

--

A humble place to learn Java and Programming better.

Recommended from Medium

Abstracting Reduce: The Case for the Monoid — A Swift Example

Deploy Containerized Flask app to Heroku

FALLEN PRODUCTION rises

CSS Combinators ♥

Configure Postgresql for Scale

slightly Blue Topaz Silver Blue Ring jewellery L-1in US 5678

CS371p Spring 2022: Week 10

Simple way to compare consumption memory in Ruby using benchmark/memory .

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
Donald Raab

Donald Raab

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

More from Medium

Map vs. Multimap

5 Constructive Java Code Review Comments That Seasoned Developers Use

MapStruct —Easy Java Mapping

ReThinking If/Else — Use the Force (part 1)