building maps in java
rzwitserloot posted in programming on September 13th, 2006
Wherein I reveal a neat little tool to create list/set/map literals for java 1.5, inspired by Erling’s comment (4th comment) from my previous article.
I assert that arrays are pretty much ‘deprecated’ in java: the collections API (lists, sets, maps) are just more flexible in every way; there is very very little ground where it makes sense to use an array because they can do stuff better. Maybe for speed reasons for computer graphics programming and other such esoteric areas.
If you haven’t yet reached that conclusion, feel free to read my reasoning. It’s the first appendix attached to this story.
The unfortunate drawback to using the collections API is that it’s just a library - the language itself offers no fancy support for any of it. For example, PHP, javascript, ruby, and python all have a fairly simple way of making a map literal. The language syntax actually has a map literal primitive. example python code:
HTTP_CODES = {404: "Not Found", 403: "Forbidden", 200: "OK"}
However, in java, such a primitive does not exist. Furthermore, the library as it exists now never actually offered a decent way of making literals. The asList method offers a decent way of making lists on the fly, as in:
ListmyList = Arrays.asList(1, 2, 3, 4, 5); List CONSTANTS = Collections.unmodifiableList(Arrays.asList(1, 2, 3, 4, 5));
But even that has problems; the returned list is array-backed, meaning that you can’t grow it. The add method throws an exception. Yet it cannot be used for constants verbatim, because setting new elements in the list is still legal, hence the need to wrap in an unmodifiableList call if thats the intent. Creating a list with a bunch of initial values which can still be grown requires this kind of tripe:
Listnumbers = new ArrayList (); numbers.addAll(Arrays.asList(1,2,3,4,5);
Ugh. two lines, can’t even be written as a simple attribute assignment anymore. ug-ly. Now try to make a map (or set) constant, and you’ll cry:
private static final MapHTTP_CODES; static { Map codes = new HashMap (); codes.put(404, “Not Found”); codes.put(403, “Forbidden”); codes.put(200, “OK”); HTTP_CODES = Collections.unmodifiableMap(codes); }
Compared to the python example you gain type safety and immutability but that’s ridiculously (and needlessly) wordy!
Here’s the fix. First, the syntax for creating one:
private static final MapHTTP_CODES = Map.put(404, “Not Found”) .put(403, “Forbidden”).put(200, “OK”).immutable();
Woohoo! one liner again!
The above assumes that sun adds the static ‘put’ method to the Map inteface. Since java 1.6 it’s legal to put static methods in interfaces. However, until that happends, here’s an almost-as-good implementation. Replace ‘Map’ with ‘MapBuilder’ above and it all works:
code for MapBuilder.java:
package example; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; public class MapBuilder{ private Map map; private boolean closed = false; private MapBuilder(Map map) { this.map = map; } public static MapBuilder linked(K key, V value) { Map map = new LinkedHashMap (); map.put(key, value); return new MapBuilder (map); } public static MapBuilder plain(K key, V value) { Map map = new HashMap (); map.put(key, value); return new MapBuilder (map); } public static MapBuilder tree(K key, V value) { Map map = new TreeMap (); map.put(key, value); return new MapBuilder (map); } public MapBuilder put(K key, V value) { if ( closed ) throw new IllegalStateException(); map.put(key, value); return this; } public Map mutable() { closed = true; return map; } public Map immutable() { closed = true; return Collections.unmodifiableMap(map); } }
Appendix 1
Why you should always use the collections API:
Since java 1.2, the so called ‘collections framework’ offers all the usual suspects in Data Structures: collections, lists, sets, and maps. Since java 1.5, you can use generics to enforce type safety and avoid unseemly casting. For example, to find the largest number amongst a set, you could try the following:
public static int max(Listlist) { int largest = Integer.MIN_INT; if ( list.size() == 0 ) throw new IllegalArgumentException(); for ( int x : list ) if ( x > largest ) largest = x; return x; }
Note how type safety is secured all around. The same thing but with arrays looks like:
public static int max(int[] list) {
int largest = Integer.MIN_INT;
if ( list.length == 0 ) throw new IllegalArgumentException();
for ( int x : list ) if ( x > largest ) largest = x;
}
Those two code snippets are virtually equal to each other. However, unlike arrays, lists can grow, they are actually more flexible, type safety wise (because arrays do not play all that nicely with generics, and generics allow for some very nifty type safety devices.
Everything an array can do can be done just as easily with a genericsified list, whereas there are loads of things lists can do that got arrays stumped. Such as, oh, say, growing on demand, or printing nicely.

September 13th, 2006 at 13:12
StringBuilder has the toString method to return the result of the building operation. It is however possible to add more data to the StringBuilder after calling toString. The StringBuilder is never closed.
I suggest you add additional methods to either ‘clone’ the builder or the return an intermediate result map without closing the builder.
You could also choose to always return a (safe) copy of the internal map.
September 13th, 2006 at 14:57
Is there a practical point to that? How about adding a putAll method to the builder? That way you can pull tricks like this:
private static final Map subMap = Map.put(key, value).put….;
private static final Map fullMap = Map.put(subMap).put(key, value).put…..;
same effect except this time around there’s no ambiguity about how multiple outputs from 1 builder act (do they back each other? Are they copies? Does building ‘reset’ back to nothing? No - either you use it ‘right’ or you get an exception. That’s the perfect library behaviour in my book provided it’s flexible. With a put(Map) method, it would be.
September 14th, 2006 at 11:40
Discussed this a bit more with Roel, and we decided it would be a good idea to change the naming of mutable() and immutable() to:
modifiableMap()
unmodifiableMap()
synchronizedMap()
The last one produces a modifiable, wrapped in Collections.synchronizedMap, map. (There’s no unmodifiable synchronized map because once the map is unmodifiable, there’s no way to blow anything up that ’synchronized’ would solve).
October 1st, 2006 at 15:32
One way is to a instance initializer block:
new HashMap() {{
put(”a”, “first”);
put(”b”, “second”);
put(”c”, “second”);
}};
Anyway you choose to do it, we’ll all just continue to cringe whenever circumventing Java’s lack for hash literals.
October 1st, 2006 at 20:52
I just noticed I lost the generics bits of the code in the sample (and I took pains to use HTML entities! I guess wordpress sodded up someplace). Just imagine the appropriate stuff in there (the first 2 elements added would be used for auto-inference, and if that doesn’t suffice, you can always manually specify it for that first call to put).
Re: Jari - huh? Is that legal in current java? Just checked, it is! Never realized you can add constructors like that to anoynymous classes. Learn something new every day.
That’s also a nice solution and doesn’t require a MapBuilder kind of class.
October 7th, 2006 at 14:22
I didn’t know Java 6 was allowed static methods in interfaces.
Do you have a reference for this?
October 7th, 2006 at 15:37
Just tested it out and …. it doesnt’ work. I swear I read on sun’s own blogs that the feature was greenlit for mustang.
I can only hope this insane little oversight has been held back because there are plans to overhaul further and allow static interface method to serve as pass-through methods for any implementing interfaces. Would remove that eye-sore Collections.sort(a) and just allow a.sort().
I can’t even find that page anymore.
October 25th, 2006 at 17:49
there is an easy mapping way in commons-lang
ArrayUtils.toMap(new String[][]
{
{ “1″, “some value” },
{ “2″, “another” },
{ “3″, “even better”} })
using arrays and the {} syntax is the easy way for variable length argument lists and it is in java since the beginning.
October 25th, 2006 at 22:29
I seriously dislike that one because it doesn’t work for different subtypes (say, mapping integers to strings). There’s also no sanity checks on sizes - I don’t think you’ll get a compile time error if you forget some braces here or there and end up with a 4-size string array someplace.
(NB: As for the first, with Object[][] you’re in the clear, but then generics won’t work).