Heeft Java een hashmap met reverse-lookup?

Ik heb gegevens die georganiseerd zijn in een soort van een “sleutel-toets” -formaat, in plaats van “sleutelwaarde”. Het is als een hasjmap, maar ik heb o (1) opzoek in beide richtingen nodig. Is er een naam voor dit type gegevensstructuur en is zoiets als dit opgenomen in de standaardbibliotheken van Java? (of misschien Apache Commons?)

Ik zou mijn eigen klasse kunnen schrijven die eigenlijk twee gespiegelde kaarten gebruikt, maar ik heb het wiel liever niet opnieuw uit (als dit al bestaat, maar ik ben gewoon niet op zoek naar de juiste termijn).


1, Autoriteit 100%

Er is geen klasse in de Java API. De Apache Commons-klasse die u wilt, wordt een van de implementaties van Bidimap .

Als een wiskundige zou ik dit soort structuur een biologie noemen.


2, Autoriteit 72%

Naast Apache Commons, guava heeft ook een bimap .


3, Autoriteit 19%

Hier is een eenvoudige klasse die ik heb gewend om dit gedaan (ik wilde geen andere afhankelijkheid van derden hebben). Het biedt niet alle functies die beschikbaar zijn in kaarten, maar het is een goede start.

   public class BidirectionalMap<KeyType, ValueType>{
        private Map<KeyType, ValueType> keyToValueMap = new ConcurrentHashMap<KeyType, ValueType>();
        private Map<ValueType, KeyType> valueToKeyMap = new ConcurrentHashMap<ValueType, KeyType>();
        synchronized public void put(KeyType key, ValueType value){
            keyToValueMap.put(key, value);
            valueToKeyMap.put(value, key);
        }
        synchronized public ValueType removeByKey(KeyType key){
            ValueType removedValue = keyToValueMap.remove(key);
            valueToKeyMap.remove(removedValue);
            return removedValue;
        }
        synchronized public KeyType removeByValue(ValueType value){
            KeyType removedKey = valueToKeyMap.remove(value);
            keyToValueMap.remove(removedKey);
            return removedKey;
        }
        public boolean containsKey(KeyType key){
            return keyToValueMap.containsKey(key);
        }
        public boolean containsValue(ValueType value){
            return keyToValueMap.containsValue(value);
        }
        public KeyType getKey(ValueType value){
            return valueToKeyMap.get(value);
        }
        public ValueType get(KeyType key){
            return keyToValueMap.get(key);
        }
    }

4, Autoriteit 9%

Als er geen botsingen optreden, kunt u altijd beide richtingen toevoegen aan dezelfde hashmap: -)


5, Autoriteit 7%

Hier mijn 2 cent.

of u kunt een eenvoudige methode gebruiken met generieke geneesmiddelen. Stukje taart.

public static <K,V> Map<V, K> invertMap(Map<K, V> toInvert) {
    Map<V, K> result = new HashMap<V, K>();
    for(K k: toInvert.keySet()){
        result.put(toInvert.get(k), k);
    }
    return result;
}

Natuurlijk moet je een kaart hebben met unieke waarden. Anders wordt een van hen vervangen.


6, Autoriteit 2%

Geïnspireerd door Getah’s antwoord Ik besloot om iets soortgelijks te schrijven door mijzelf met enkele verbeteringen:

  • De klasse implementeert de Map<K,V>-Interface
  • De bidirectionaliteit is echt gegarandeerd door ervoor te zorgen bij het wijzigen van een waarde door een put(ik hoop het ten minste hierbij te garanderen)

Gebruik is net als een normale kaart, om een ​​omgekeerde weergave te krijgen op de telefoongesprek getReverseView(). De inhoud wordt niet gekopieerd, alleen een weergave wordt geretourneerd.

Ik weet niet zeker of dit helemaal fooldicht is (eigenlijk is het waarschijnlijk niet), dus voel je vrij om commentaar te geven als je fouten opmerkt en ik zal het antwoord bijwerken.

public class BidirectionalMap<Key, Value> implements Map<Key, Value> {
    private final Map<Key, Value> map;
    private final Map<Value, Key> revMap;
    public BidirectionalMap() {
        this(16, 0.75f);
    }
    public BidirectionalMap(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }
    public BidirectionalMap(int initialCapacity, float loadFactor) {
        this.map = new HashMap<>(initialCapacity, loadFactor);
        this.revMap = new HashMap<>(initialCapacity, loadFactor);
    }
    private BidirectionalMap(Map<Key, Value> map, Map<Value, Key> reverseMap) {
        this.map = map;
        this.revMap = reverseMap;
    }
    @Override
    public void clear() {
        map.clear();
        revMap.clear();
    }
    @Override
    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }
    @Override
    public boolean containsValue(Object value) {
        return revMap.containsKey(value);
    }
    @Override
    public Set<java.util.Map.Entry<Key, Value>> entrySet() {
        return Collections.unmodifiableSet(map.entrySet());
    }
    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }
    @Override
    public Set<Key> keySet() {
        return Collections.unmodifiableSet(map.keySet());
    }
    @Override
    public void putAll(Map<? extends Key, ? extends Value> m) {
        m.entrySet().forEach(e -> put(e.getKey(), e.getValue()));
    }
    @Override
    public int size() {
        return map.size();
    }
    @Override
    public Collection<Value> values() {
        return Collections.unmodifiableCollection(map.values());
    }
    @Override
    public Value get(Object key) {
        return map.get(key);
    }
    @Override
    public Value put(Key key, Value value) {
        Value v = remove(key);
        getReverseView().remove(value);
        map.put(key, value);
        revMap.put(value, key);
        return v;
    }
    public Map<Value, Key> getReverseView() {
        return new BidirectionalMap<>(revMap, map);
    }
    @Override
    public Value remove(Object key) {
        if (containsKey(key)) {
            Value v = map.remove(key);
            revMap.remove(v);
            return v;
        } else {
            return null;
        }
    }
}

Other episodes