0

I've inserted values into a Map using Multikey with 3 params. Now I would like to retrieve all the entries that have a specific key in their Multikey -and I've no idea as to values of the other 2 ..

map.put(new MultiKey(valueA,valueB,valueC), value);

Now only have valueA , I need to retrieve value

If clarification is required, please ask and I'll elaborate rather than voting to close.. Thanks :)

..

akapulko2020
  • 1,079
  • 2
  • 22
  • 36
  • 2
    would you please post the code (so far what have you done) ? – 1000111 Mar 11 '15 at 06:56
  • I think you're misunderstanding how Multikey works. Using it as a key does not mean that **any** of the keys you've given point to a value, but that the **combination of all of them** does. – Mureinik Mar 11 '15 at 07:07
  • 3
    Iterate over the entries and filter them. Maybe what you need is a tree, or multiple maps. – JB Nizet Mar 11 '15 at 07:13
  • @Mureinik nope, I understand the principle. Just hoped there is a way to code over it :) – akapulko2020 Mar 11 '15 at 07:14
  • @JBNizet can't change the type of container, it has to be a HashMap... How do you mean -iterate ? Just go over all the entries in Map? Hmm. Thanks! – akapulko2020 Mar 11 '15 at 07:15

2 Answers2

1

I've actually created a class just for this.

public class MultiMap<K, V> {

    private final HashMap<KeySet<K>, V> model = new HashMap<>();

    public MultiMap() {}

    public V add(V value, K first, K... keys) {
        return model.put(new KeySet<>(first, keys));
    }

    public V add(V value, Set<K> keys){
        return model.get(new KeySet<>(keys));
    }

    public Set<V> getIncludingSubsets(K... keys){
        HashSet<V> all = new HashSet<>();
        for (Entry<KeySet<K>, V> entry : model.entrySet()) {
            if (entry.getKey().containsPartially(keys)) {
                all.add(entry.getValue());
            }
        }
        return all;
    }    

    public Set<V> getIncludingSubsets(Set<K> keys){
        HashSet<V> all = new HashSet<>();
        for (Entry<KeySet<K>, V> entry : model.entrySet()) {
            if (entry.getKey().containsPartially(keys)) {
                all.add(entry.getValue());
            }
        }
        return all;
    }

    public void clear(){
        model.clear();
    }

    private class KeySet<T> extends HashSet<T>{

        private KeySet(T first, T ... rest){
            super();
            add(first);
            for (T object : rest) {
                add(object);
            }
        }

        private KeySet(Collection<T> data){
            super(data);
        }

        @Override
        public int hashCode() {
            int hash = 5;
            for (T value : this) {
                hash = 41 * hash + Objects.hashCode(value);
            }
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final KeySet<?> other = (KeySet<?>) obj;
            return hashCode() == other.hashCode();
        }

        public boolean containsPartially(T... values){
            for (T value : values) {
                if (!contains(value)) {
                    return false;
                }
            }
            return true;
        }

        public boolean containsPartially(Set<T> values){
            for (T value : values) {
                if (!contains(value)) {
                    return false;
                }
            }
            return true;
        }
    }
}

Here is some sample code illustrating how it works:

MultiMap<String, String> mm = new MultiMap<>();

// "A","B","C" -> v1
// "A","B" -> v2
// "A","C" -> v3

mm.add("v1", "A", "B", "C");
mm.add("v2", "A", "B");
mm.add("v3", "A", "C");

System.out.println(mm.getIncludingSubsets("A", "C"));
System.out.println(mm.getIncludingSubsets("B"));
System.out.println(mm.getIncludingSubsets("C"));
System.out.println(mm.getIncludingSubsets("C", "B", "A"));
System.out.println(mm.getIncludingSubsets("B", "A"));
System.out.println(mm.getIncludingSubsets("B", "C"));

This will produce:

[v1, v3]
[v1, v2]
[v1, v3]
[v1]
[v1, v2]
[v1]

I'm sure with a little creativity you can change this implementation so it extends a HashMap rather than having one as a field.

RDM
  • 4,986
  • 4
  • 34
  • 43
1

Something as simple as

static <V> List<V> getValues(Map<MultiKey, V> map, Object key1) {
  List<V> values = new ArrayList<>();
  for (Map.Entry<MultiKey, V> entry : map.entrySet()) {
    MultiKey key = entry.getKey();
    if (key.getKey(0).equals(key1)) {
      values.add(entry.getValue());
    }
  }
  return values;
}

could work just fine. Extending that to use more than one (key1, key2, ...) is left as an exercise to the reader.

mrjink
  • 1,131
  • 1
  • 17
  • 28