Map builder

I seem to build a lot of maps, and I hate the multi-line verbosity. So I made a map builder to turn mapping into a one-liner affair.


package org.example.misc;

import java.util.HashMap;
import java.util.Map;

public class Maps {

    public static <Q,W> MapWrapper<Q,W> map(Q q, W w) {
        return new MapWrapper<Q, W>(q, w);
    }

    public static final class MapWrapper<Q,W> {
        private final HashMap<Q,W> map;
        public MapWrapper(Q q, W w) {
            map = new HashMap<Q, W>();
            map.put(q, w);
        }
        public MapWrapper<Q,W> map(Q q, W w) {
            map.put(q, w);
            return this;
        }
        public Map<Q,W> getMap() {
            return map;
        }
    }

    public static void main(String[] args) {
        Map<String, Integer> map = Maps.map("one", 1).map("two", 2).map("three", 3).getMap();
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }
    }
}

Even less code by extending HashMap!


package org.example.misc;

import java.util.HashMap;
import java.util.Map;

public class Maps {

    public static <Q,W> MyHashMap<Q,W> map(Q q, W w) {
        return new MyHashMap<Q, W>(q, w);
    }

    public static final class MyHashMap<Q, W> extends HashMap<Q, W>{

        public MyHashMap(Q q, W w) {
            put(q, w);
        }

        public MyHashMap<Q,W> map(Q q, W w) {
            put(q, w);
            return this;
        }
    }

    public static void main(String[] args) {
        Map<String, Integer> map = Maps.map("one", 1).map("two", 2).map("three", 3);
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }
    }
}

Not the most type safe thing I’ve written, but here it is interleaved:

import java.util.HashMap;
import java.util.Map;

public class Maps {
	
	@SuppressWarnings("unchecked")
	public static <K, V> Map<K, V> map(Object... a) {
		if (a.length % 2 != 0)
			throw new IllegalArgumentException("Keys and Values must be in pairs");
		
		Map<K, V> map = new HashMap<>();
		
		for (int i = 0; i < a.length;)
			map.put((K) a[i++], (V) a[i++]);
		
		return map;
	}
	
	public static void main(String[] a) {
		Map<String, Integer> map = Maps.map("one", 1, "two", 2, "three", 3);
		
		for (Map.Entry<String, Integer> entry : map.entrySet())
			System.out.println(entry.getKey() + " = " + entry.getValue());
		
	}
}

Little more verbose to call map(), but at least it will throw an error on wrongly typed args:

import java.util.HashMap;
import java.util.Map;

public class Maps {
	
	@SuppressWarnings("unchecked")
	public static <K, V> Map<K, V> map(Class<K> kc, Class<V> vc, Object... a) {
		if (a.length % 2 != 0)
			throw new IllegalArgumentException("Keys and Values must be in pairs");
		
		Map<K, V> map = new HashMap<>();
		
		for (int i = 0; i < a.length;) {
			Object k = a[i++], v = a[i++];
			if (!kc.isInstance(k))
				throw new IllegalArgumentException("Wrong typed argument: " + k);
			if (!vc.isInstance(v))
				throw new IllegalArgumentException("Wrong typed argument: " + v);
			map.put((K) k, (V) v);
		}
		
		return map;
	}
	
	public static void main(String[] a) {
		Map<String, Integer> map = Maps.map(String.class, Integer.class, "one", 1, "two", 2, "three", 3);
		
		for (Map.Entry<String, Integer> entry : map.entrySet())
			System.out.println(entry.getKey() + " = " + entry.getValue());
		
	}
}

Another approach using the little-known double-brace initialization technique:

private final Map map = new HashMap() {{ put( "one", 1 ); put( "two", 2 ); }};

Slightly more verbose in that you have to replicate the key/value classes since the created map is actually an anonymous class.

Doesn’t really save much, plus it’s not 1 line, and it creates anonymous class. I try to avoid double-brace.

I’ve read that extending the collections classes is bad practice…? But sometimes I want to build different maps like TreeMaps so I prefer not to extend but wrap instead. Then I could do something like:

 Maps.treeMap("one", 1).map(...).getMap();