We often have to work with a defined collection of values in our programs. Maps are one way of holding those defined values and retrieving those by names when we need to use. One easy way of reusing those predefined values have been using static maps in a class so that we don’t instantiate those same values again and again every time we use it. That’s a very basic and minimal thing to start with for any program.
But as your system goes bigger and you start sharing code among many team members and many modules of your system, and as performance issues become more important than anything else, and on top of it if you are working on multi-threaded environments – you know you have to play the game at a higher level than this.
We recently hit a relevant bug around using static hashmap that led me to refactor and restructure a similar basic code to a more sophisticated fixed collection. I learnt that the use case for a completely fixed map is not well understood and clear to everyone. At the end of the analysis and refactoring what I came up with can be simplified to a small program that I developed for my readers here.
Please look at my public github repository for the source code while I walk you through it – https://github.com/ashikuzzaman/javashare
There are two major ways of generating a fixed map other than writing your own collection data structure. First option is what comes with Java Standard Edition – an API to make any collection unmodifiable that gives you a read-only view of a modifiable collection. For Map it is Collections.unmodifiableMap(map).
Second option to make a map fixed we get from Google’s Guava library – Immutable collections. For Map, it is ImmutableMap which can be used instead of HashMap that we use more often in simple java programs. It makes sure once you build the map, you can no more add, update or remove any entry from it. While both of these give you a simplistic fixed size map, they have important behavioral difference. Additionally, none of them can guarantee you a truly fixed map where not only entries can’t be manipulated but each value for each entry also can’t be manipulated either. Let’s see where the gap is with them and how we can overcome the gap.
In FixedMap.java, FixedContentMap.java and FixedMapRunner.java from the above project, we are working with a modifiable map, an unmodifiable map (that wraps around the modifiable map) and an immutable map (that picks the values of the modifiable map to build itself). Both unmodifiable map and immutable map, once built, will not allow you to add any new element, remove any existing element or update any element with a new value using their own references. But we can add any number of elements to modifiable map even after the other 2 maps are built out of it. And that’s where the difference between unmodifiable map and immutable map becomes evident. Look at the program output for first 4 print statements and see where unmodifiable map falls short. While immutable map wont change in size even if you add new entry to the modifiable map out of which it was built, the unmodifiable map will increase in size as long as you are using a different entry point (other than the reference variable of unmodifiable map itself) to the modifiable map to add new elements. So clearly, Immutable map has an edge over Unmodifiable map when it comes to working with fixed maps.
However, the next 4 print statements (5 to 8) show the shortcomings of both unmodfiable and immutable maps. When you say the map is immutable, you are only making sure no add, delete or update happens at an element level, more precisely, using the key of the element. But nothing is stopping you from adding a new element inside the values of a map, if the value itself is a collection. So in our case each map element values are individual set of Strings. So nothing is stopping the program to add values inside those Strings using the unmodifiable or immutable map references. This is the result of famous copy by value and copy by reference debate of Object Oriented Programming. And hence you see new values got added for “animals” as part of #7 and #8 prints. So despite Immutable map doing a better job than Unmodifiable map, I call it a fake fixed map. A true fixed map, better if I coin a term fixed content map, needs to ensure that even the individual values for each element is untouchable (i.e. read only).
To achieve that we extend FixedContentMap from FixedMap (because all fixed content map is a fixed map but some fixed maps may be fake fixed maps!). We just build the values in fixed content map as immutable sets. So now if anyone wants to change the value of the fixed content map, including the contents of any element – they will face a java.lang.UnsupportedOperationException as is evident when you run the program (you won’t see print statement #12 ever getting executed).
So my conclusion is, if used correctly, Google’s Guava library will allow you to work with truly fixed content collections in Java.