~/home of geeks

MapBuilder

· 577 Wörter · 3 Minute(n) Lesedauer

Dieser Artikel ist Teil der Artikel-Serie "MapBuilder".

patterns in nature

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;
        }
    }
}