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.