sclass DoubleKeyedMap { new Map> map1; new Map> map2; Map getA(A a) { ret map1.get(a); } Map getB(B b) { ret map2.get(b); } Cl valuesForA(A a) { ret values(getA(a)); } Cl valuesForB(B b) { ret values(getB(b)); } Set bsForA(A a) { ret keys(getA(a)); } Set asForB(B b) { ret keys(getB(b)); } C get(A a, B b) { ret mapGet(map1.get(a), b); } C get(Pair p) { ret p == null ? null : get(p.a, p.b); } C put(A a, B b, C c) { mapGetOrCreateHashMap(map1, a).put(b, c); ret mapGetOrCreateHashMap(map2, b).put(a, c); } C put(Pair p, C c) { ret put(p.a, p.b, c); } C remove(A a, B b) { nestedMapRemove(map1, a, b); ret nestedMapRemove(map2, b, a); } Set aKeys() { ret keys(map1); } Set bKeys() { ret keys(map2); } void removeAllA(Iterable l) { fOr (A a : l) removeA(a); } void removeA(A a) { fOr (B b : cloneList(bsForA(a))) remove(a, b); } }