Häufig brauche ich eine statische, unveränderliche Lookup-Map für Konstanten. Da die Java-API keine Fluent Builder hierfür enthält, habe ich einen kleinen MapBuilder geschrieben, der bei Bedarf eingesetzt werden kann.
Üblicherweise endet das in einem statischen Kodeblock, der die Initialisierung übernimmt:
private static final Map<String, String> LOOKUP;
static{
final Map<String, String> tmpLookup = new HashMap<String, String>();
tmpLookup.put("mykey1", "myvalue1");
tmpLookup.put("mykey1", "myvalue1");
LOOKUP = Collections.unmodifiableMap(tmpLookup);
}
Eine Übersicht über alternative Strategien (und einer fast gleichen Klasse wie meiner) kann man im Artikel Java 8, Initializing Maps in the Smartest Way von Per-Åke Minborg finden.
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
/**
* Generic MapBuilder
* <p/>
* Usage example:<br/>
* <code>
* Map<String, String> result = new MapBuilder<String, String>(new HashMap<String, String>())
* .put("mykey1", "myvalue1")
* .put("mykey2", "myvalue2")
* .unmodifiable()
* .build();
* </code>
*
* @author Serhat Cinar
*/
public class MapBuilder<K, V> {
private final Map<K, V> targetMap;
private boolean unmodifiable; // false
/**
* Default constructor using {@link HashMap} as the underlying map.
*/
public MapBuilder() {
this(new HashMap<K, V>());
}
public MapBuilder(final Map<K, V> targetMap) {
this.targetMap = targetMap;
}
/**
* Defines that the resulting Map should be unmodifiable as
* done by {@link Collections#unmodifiableMap(Map)}.<br/>
* Default value is {@code false}.
*
* @param unmodifiable
* @return
*/
public MapBuilder<K, V> unmodifiable(final boolean unmodifiable) {
this.unmodifiable = unmodifiable;
return this;
}
/**
* Sets the {@link #unmodifiable(boolean)} to true.
*
* @return
*/
public MapBuilder<K, V> unmodifiable() {
return unmodifiable(true);
}
public MapBuilder<K, V> put(final K key, final V value) {
targetMap.put(key, value);
return this;
}
public MapBuilder<K, V> putAll(final Map<K, V> valueMap) {
targetMap.putAll(valueMap);
return this;
}
/**
* Expects both arrays to be equally long.
*
* @param keys
* @param values
* @return
*/
public MapBuilder<K, V> putAll(final K[] keys, final V[] values) {
for (int i = 0; i < keys.length; i++) {
put(keys[i], values[i]);
}
return this;
}
/**
* Expects both Collections to have equal amounts of objects.
*
* @param keys
* @param values
* @return
*/
public MapBuilder<K, V> putAll(final Collection<K> keys, final Collection<V> values) {
Iterator<K> keyIterator = keys.iterator();
Iterator<V> valuesIterator = values.iterator();
while (keyIterator.hasNext()) {
put(keyIterator.next(), valuesIterator.next());
}
return this;
}
/**
* Expects an array of arrays.<br/>
* The inner array must always have length 2 and the first element
* must be the key, while the second one is the value.<br/>
* Example:<br/>
* <code>
* Object[][] keyValues = {{"key1", "value1"}, {"key2", "value2"}};
* </code>
*
* @param keyValues
* @return
*/
public MapBuilder<K, V> putAll(final Object[][] keyValues) {
for (int i = 0; i < keyValues.length; i++) {
Object[] data = keyValues[i];
put((K) data[0], (V) data[1]);
}
return this;
}
/**
* Expects an array of keys and values,
* where each key is followed by its value.<br/>
* Logically the length of the array must be even (dividable by 2).<br/>
* Example:<br/>
* <code>
* Object[] keyValues = {"key1", "value1", "key2", "value2"};
* </code>
*
* @param keyValues
* @return
*/
public MapBuilder<K, V> putAll(final Object[] keyValues) {
for (int i = 0; i < keyValues.length; i += 2) {
put((K) keyValues[i], (V) keyValues[i + 1]);
}
return this;
}
/**
* Returns the underlying Map.<br/>
* If {@link #unmodifiable(boolean)} was used to set {@code true},
* the returned Map
* will be wrapped by {@link Collections#unmodifiableMap(Map)} .
*
* @return
*/
public Map<K, V> build() {
if (unmodifiable) {
return Collections.unmodifiableMap(targetMap);
} else {
return targetMap;
}
}
}