-1

I want to have a Treemap with two keys and it can be called by single key.

Example: key1 is string and key2 is integer and the value is an Object.

Example for data:

{('alice', 124221, obj1), ('bob', 241241, obj2), .... }
getByString('alice') ==> obj1
getByInt(124221) ==> obj1

Note: it is never required to use the both keys at the same time to get object. One is enough

Questions:

Can it be implemented in one map? If yes, can it be guaranteed to have O(log n) time complexity for getting the value by either keys?

ldz
  • 2,217
  • 16
  • 21
vortex
  • 117
  • 7
  • Two keys [isn't a problem](https://stackoverflow.com/questions/1237581/need-a-java-map-table-with-multiple-keys-to-one-value-value-is-commonly-altered), but keys of different types is going to hurt. You'll either need to use a raw TreeMap (just don't) or write some sort of wrapper class. – azurefrog Dec 05 '19 at 20:07
  • Sure. And if you use a HashMap, it will even be O(1). All you need is to put the value twice: once for each key. – JB Nizet Dec 05 '19 at 20:08
  • @JBNizet the order of the keys are important. Then, I think I should use only a TreeMap. – vortex Dec 05 '19 at 20:11
  • Ah, then you'll need two maps, or a comparator able to compare strings with integers. – JB Nizet Dec 05 '19 at 20:15
  • Two maps is the best way. This topic was already disscused [here](https://stackoverflow.com/questions/822322/how-to-implement-a-map-with-multiple-keys). – Vlad Zaichyk Dec 05 '19 at 20:18
  • @JBNizet, sorry, the order in integer is key is important not in the string. – vortex Dec 05 '19 at 20:20
  • Does this answer your question? [Multiple indexes for a Java Collection - most basic solution?](https://stackoverflow.com/questions/2501449/multiple-indexes-for-a-java-collection-most-basic-solution) – Vlad Zaichyk Dec 05 '19 at 20:21

1 Answers1

0

As @azurefrog points out, the behaviour you want isn't provided by TreeMap directly, so you will need a wrapper class of some sort. Here's an example; I've used TreeMaps for the internal implementation, but unless you want to use the ordering somehow with more methods, it's better to use a HashMap instead since then you get O(1) get and put operations, instead of O(log n).

import java.util.Map;
import java.util.TreeMap;

public class TwoKeysMap<K1 extends Comparable<K1>, K2 extends Comparable<K2>, V> {
    private static class Entry<K1 extends Comparable<K1>, K2 extends Comparable<K2>, V> {
        private final K1 k1;
        private final K2 k2;
        private final V v;

        private Entry(K1 k1, K2 k2, V v) {
            this.k1 = k1;
            this.k2 = k2;
            this.v = v;
        }
    }

    private final Map<K1, Entry<K1, K2, V>> map1 = new TreeMap<>();
    private final Map<K2, Entry<K1, K2, V>> map2 = new TreeMap<>();

    public V getByKey1(K1 k1) {
        Entry<K1, K2, V> e = map1.get(k1);
        return e != null ? e.v : null;
    }

    public V getByKey2(K2 k2) {
        Entry<K1, K2, V> e = map2.get(k2);
        return e != null ? e.v : null;
    }

    public void put(K1 k1, K2 k2, V v) {
        Entry<K1, K2, V> e = new Entry<>(k1, k2, v);
        map1.put(k1, e);
        map2.put(k2, e);
    }
}

Usage:

> TwoKeysMap<String, Integer, Object> map = new TwoKeysMap<>();
> map.put("alice", 124221, "Object 1");
> map.put("bob", 241241, "Object 2");
> map.getByKey1("alice")
"Object 1" (Object)
> map.getByKey2(124221)
"Object 1" (Object)

Note that the inner Entry class isn't strictly needed for just "get" and "put", but if you want to remove by key1 then the entry needs to store key2 too so you can remove from both maps, and vice versa:

    public void removeByKey1(K1 k1) {
        Entry<K1, K2, V> e = map1.remove(k1);
        if(e != null) {
            map2.remove(e.k2);
        }
    }

    public void removeByKey2(K2 k2) {
        Entry<K1, K2, V> e = map2.remove(k2);
        if(e != null) {
            map1.remove(e.k1);
        }
    }
kaya3
  • 47,440
  • 4
  • 68
  • 97