2

I'm looking for an implementation of MultiKey (actually DoubleDouble) to single Value. * BUT * you can access the value via ONE OF THE KEYS! meaning, It's not mandatory to have both keys in order to access the map.

I know I can write something to fulfill my request - but the question is if there is something out there that is already written so I can use it out-of-the-box.

Thanks :-)

EDIT: At this point the best implementation I can think of is this:

class DoubleKeyHashMap<K1, K2, V> {
    BiMap<K1, K2> keys; // Bidirectional map
    Map<K2, V> values;
..
..
}
Shvalb
  • 1,835
  • 2
  • 30
  • 60
  • I asked this here long time ago and did not find anything, I don't think it still exists. Wow it was six years ago: [Any implementation of Map, i.e. two keys?](http://stackoverflow.com/questions/311103/any-implementation-of-mapk1-k2-v-i-e-two-keys) – Miserable Variable Jan 21 '16 at 21:19
  • So technically this is a duplicate but I am not going mark it as such in the hope that someone now has a better answer – Miserable Variable Jan 21 '16 at 21:21
  • Simply create your own `Key` class with the semantics you desire. – Mick Mnemonic Jan 21 '16 at 21:22
  • @MickMnemonic you need a `Map` class, not a `Key` class – Miserable Variable Jan 21 '16 at 21:27
  • I guess there isn't anything like that out there..... – Shvalb Jan 21 '16 at 21:29
  • 2
    Possible duplicate of [How to implement a Map with multiple keys?](http://stackoverflow.com/questions/822322/how-to-implement-a-map-with-multiple-keys) – Mick Mnemonic Jan 21 '16 at 21:29
  • You could use Guava's [`Table`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html) class. – Mick Mnemonic Jan 21 '16 at 21:31
  • 3
    Isn't that just the same as two maps `K1->V` and `K2->V`? – Henry Jan 21 '16 at 21:38
  • 4
    If the map is by two keys (k1 and k2), and you only lookup by one key value (k2), what is your expected return value? A list of all values with matching k2? If so, then something akin to a SQL table with multiple indexes would be required (for performance), in which case it's not a `Map` at all. – Andreas Jan 21 '16 at 21:42
  • Just wanted to write a similar answer to @Andreas - such object most likely wouldn't be a `Map`. A `Map` maps the exact key with the exact value. – AndrewMcCoist Jan 21 '16 at 21:47
  • all the answers with 2 maps are good, but a bit expensive both in space and in management. and doesn't see to be elegant. Look at the "EDIT". – Shvalb Jan 21 '16 at 22:00
  • 1
    @Shvalb Maps don't allow multiple values for a key, so your edit won't work, unless all objects have distinct k1 values and distinct k2 values, or to use the SQL analogy, if both the k1 index and the k2 index are *unique*. Nothing in your question said that was the case. If they are independently unique, then a two-map solution is the best, and I'm not sure why you consider that "expensive". – Andreas Jan 21 '16 at 23:45
  • @Andreas I'm not going to have multiple values, only Double unique keys for each value. – Shvalb Jan 22 '16 at 15:10

1 Answers1

0

This seems like a good start to a multi key map implementation.

Edited to add a removeElement method, and to save and return a List of values.

package com.ggl.testing;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MultiMap<K, V> {

    private static long sequence = 0;

    private Map<K, Long> key1Map;
    private Map<K, Long> key2Map;
    private Map<Long, List<V>> valueMap;

    public MultiMap() {
        this.key1Map = new HashMap<>();
        this.key2Map = new HashMap<>();
        this.valueMap = new HashMap<>();
    }

    public void addElement(K key1, K key2, V value) {
        boolean key1boolean = key1Map.containsKey(key1);
        boolean key2boolean = key2Map.containsKey(key2);
        boolean key3boolean = key1Map.containsKey(key2);
        boolean key4boolean = key2Map.containsKey(key1);

        if (key1boolean && key2boolean) {
            Long key1Value = key1Map.get(key1);
            Long key2Value = key2Map.get(key2);
            updateValue(key1, key2, key1Value, key2Value, value);
        } else if (key3boolean && key4boolean) {
            Long key1Value = key1Map.get(key2);
            Long key2Value = key2Map.get(key1);
            updateValue(key1, key2, key1Value, key2Value, value);
        } else if (key1boolean || key4boolean) {
            String s = displayDuplicateError(key1);
            throw new IllegalStateException(s);
        } else if (key2boolean || key3boolean) {
            String s = displayDuplicateError(key2);
            throw new IllegalStateException(s);
        } else {
            createValue(key1, key2, value);
        }

    }

    private void createValue(K key1, K key2, V value) {
        Long newKeyValue = sequence++;
        key1Map.put(key1, newKeyValue);
        key2Map.put(key2, newKeyValue);

        List<V> values = new ArrayList<>();
        values.add(value);
        valueMap.put(newKeyValue, values);
    }

    private void updateValue(K key1, K key2, Long key1Value, Long key2Value,
            V value) {
        if (key1Value.equals(key2Value)) {
            List<V> values = valueMap.get(key1Value);
            values.add(value);
            valueMap.put(key1Value, values);
        } else {
            String s = displayMismatchError(key1, key2);
            throw new IllegalStateException(s);
        }
    }

    private String displayMismatchError(K key1, K key2) {
        return "Keys " + key1.toString() + " & " + key2.toString()
                + " have a different internal key.";
    }

    private String displayDuplicateError(K key) {
        return "Key " + key.toString() + " is part of another key pair";
    }

    public List<V> getElement(K key) {
        if (key1Map.containsKey(key)) {
            return valueMap.get(key1Map.get(key));
        }

        if (key2Map.containsKey(key)) {
            return valueMap.get(key2Map.get(key));
        }

        return null;
    }

    public boolean removeElement(K key) {
        if (key1Map.containsKey(key)) {
            Long key1Value = key1Map.get(key);
            Set<Entry<K, Long>> entrySet = key2Map.entrySet();
            K key2 = getOtherKey(key1Value, entrySet);

            valueMap.remove(key1Value);
            key1Map.remove(key);
            key2Map.remove(key2);

            return true;
        } else if (key2Map.containsKey(key)) {
            Long key2Value = key2Map.get(key);
            Set<Entry<K, Long>> entrySet = key1Map.entrySet();
            K key1 = getOtherKey(key2Value, entrySet);

            valueMap.remove(key2Value);
            key1Map.remove(key1);
            key2Map.remove(key);

            return true;
        }

        return false;
    }

    private K getOtherKey(Long key1Value, Set<Entry<K, Long>> entrySet) {
        Iterator<Entry<K, Long>> iter = entrySet.iterator();
        K key = null;
        while (iter.hasNext() && key == null) {
            Entry<K, Long> entry = iter.next();
            if (entry.getValue().equals(key1Value)) {
                key = entry.getKey();
            }
        }
        return key;
    }

    public static void main(String[] args) {
        MultiMap<String, String> multiMap = new MultiMap<>();

        try {
            multiMap.addElement("one", "two", "numbers");
            multiMap.addElement("alpha", "beta", "greek alphabet");
            multiMap.addElement("beta", "alpha", "alphabet");
            multiMap.addElement("iron", "oxygen", "elements");
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(Arrays.toString(multiMap.getElement("iron")
                .toArray()));
        System.out.println(Arrays.toString(multiMap.getElement("beta")
                .toArray()));

        System.out.println(multiMap.removeElement("two"));
    }

}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
  • What do you think about the "EDIT" that appear above. it uses BiMap and HashMap. it's a solution designed only for DoubleKeys - not a general MultiKeys. – Shvalb Jan 22 '16 at 15:13
  • I'll try to share it on Github. – Shvalb Jan 26 '16 at 16:55