All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.hypfvieh.collections.BidiMap Maven / Gradle / Ivy

Go to download

A collection of utils commonly used in my projects. Feel free to use it (or parts of it) in your own projects.

The newest version!
package com.github.hypfvieh.collections;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * A bidirectional map.
 * If created from an existing map, the original map is not modified.
 * Instead a new Map objects of the same type
 * are created for a copy of the original map and an inverted map.
 * Should this fail, the type defaults to {@link LinkedHashMap}.
 *
 * Combinations of key-value must be unique in order to create and
 * maintain the corresponding inverse map. In addition, null is not accepted
 * as a valid value, as it is not permitted as a map key.
 * In these cases {@link IllegalArgumentException} is thrown.
 *
 * The map requires external synchronization.
 *
 */
public class BidiMap implements Map {

    private final Map map;
    private final Map inverseMap;

    public BidiMap() {
        this(null);
    }

    @SuppressWarnings("unchecked")
    public BidiMap(Map _map) {
        map = (Map) createMap(_map);
        inverseMap = (Map) createMap(map);
        if (_map != null) {
            putAll(_map);
        }
    }

    @Override
    public V get(Object _key) {
        checkParms(_key);
        return map.get(_key);
    }

    public K getKey(V _val) {
        checkParms(_val);
        return inverseMap.get(_val);
    }

    @Override
    public boolean containsKey(Object _key) {
        checkParms(_key);
        return map.containsKey(_key);
    }

    @Override
    public boolean containsValue(Object _val) {
        checkParms(_val);
        return inverseMap.containsKey(_val);
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public Set keySet() {
        // prevent modifications using the keySet
        return Collections.unmodifiableSet(map.keySet());
    }

    @Override
    public Collection values() {
        // prevent modifications using the values collection
        return Collections.unmodifiableCollection(map.values());
    }

    @Override
    public Set> entrySet() {
        // prevent modifications using the entry set
        return Collections.unmodifiableSet(map.entrySet());
    }

    @Override
    public V remove(Object _key) {
        checkParms(_key);
        V removedValue = map.remove(_key);
        if (removedValue != null) {
            inverseMap.remove(removedValue);
        }
        return removedValue;
    }

    /**
     * Removes the mapping for a value from this map if it is present.
     *
     * @param _val value whose mapping is to be removed from the map
     * @return the previous key associated with value.
     */
    public K removeValue(V _val) {
        checkParms(_val);
        K removedKey = inverseMap.remove(_val);
        if (removedKey != null) {
            map.remove(removedKey);
        }
        return removedKey;
    }

    @Override
    public V put(K _key, V _val) {
        checkParms(_key, _val);

        K invKey = inverseMap.get(_val);
        if (invKey == null) { // value does not exist in map
            if (containsKey(_key)) { // prevent storage of different values under same key
                throw new IllegalArgumentException("Key [" + _key + "] not unique in bidirectional map, cannot put(" + _key + ", " + _val + ").");
            }
        } else { // value exists in map
            if (!_key.equals(invKey)) { // prevent storage of same value under different key
                throw new IllegalArgumentException("Value [" + _val + "] not unique in bidirectional map, cannot put(" + _key + ", " + _val + ").");
            }
        }

        V prevVal = map.put(_key, _val);
        inverseMap.put(_val, _key);
        return prevVal;
    }

    @Override
    public void putAll(Map _map) {
        for (java.util.Map.Entry entry : _map.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    @Override
    public void clear() {
        map.clear();
        inverseMap.clear();
    }

    @Override
    public final String toString() {
        return getClass().getName() + "[" + map + "]";
    }

    static Map createMap(Map _map) {
        Map newMap;
        try {
            newMap = _map.getClass().getDeclaredConstructor().newInstance();
        } catch (Exception _ex) {
            newMap = new LinkedHashMap<>();
        }
        return newMap;
    }

    static void checkParms(Object... _objs) {
        if (_objs != null) {
            for (Object obj : _objs) {
                if (obj == null) {
                    throw new IllegalArgumentException("Null parameter not allowed.");
                }
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy