我知道Guava
内部有一个BiMultimap类,但没有外包代码。我需要一个双向的数据结构,即按键和按值查找,也接受重复。
即。像这样:(在我的例子中,值是唯一的,但两个值可以指向相同的键)
0 <-> 5
1 <-> 10
2 <-> 7
2 <-> 8
3 <-> 11
我希望能够get(7)
- &gt;返回2
和get(2)
返回[7, 8]
。
那里有另一个库,它有一个我可以使用的数据结构吗?
如果没有,你认为处理这种情况的更好选择是什么?是将两个Multimaps
保留在记忆中,另一个是不良做法吗?
P.S。:我读过这个问题:Bidirectional multi-valued map in Java但是考虑到它是在2011年,我想我会打开一个更近期的问题
答案 0 :(得分:1)
你是什么意思
Guava内部有一个BiMultimap类,但没有外包代码
code of an implementation is here。
我没有检查这是否是一个有效的实现,也没有检查它是否是一个版本,或者我只是在看某种快照。一切都在公开场合,所以你应该能够得到它。
从快速浏览源代码看,实现确实维护了两个MultMaps,这对于一般情况应该没问题。
答案 1 :(得分:0)
如果您不需要一大堆Guava HashBiMultimap功能,只需要getByKey()和getByValue(),就像您指定的那样,我可以建议这种方法,其中只有一个HashMultiMap用作存储。
我们的想法是将提供的键和值视为均衡对象,并将它们作为键和值放在存储映射中。
例如:让我们拥有以下multiMap.put(0, 5)
,因此我们应该获取包含此类[[key:0, value:5], [key:5, value:0]]
之类的存储地图。
只要我们需要BiMultiMap是通用的,我们还需要提供一些包装类,它们应该用作存储地图类型参数。
以下是这个包装类:
public class ObjectHolder {
public static ObjectHolder newLeftHolder(Object object) {
return new ObjectHolder(object, false);
}
public static ObjectHolder newRightHolder(Object object) {
return new ObjectHolder(object, true);
}
private Object object;
private boolean flag;
private ObjectHolder(Object object, boolean flag) {
this.object = object;
this.flag = flag;
}
public Object getObject() {
return object;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ObjectHolder)) return false;
ObjectHolder that = (ObjectHolder) o;
if (flag != that.flag) return false;
if (!object.equals(that.object)) return false;
return true;
}
@Override
public int hashCode() {
int result = object.hashCode();
result = 31 * result + (flag ? 1 : 0);
return result;
}
}
这是MultiMap:
public class BiHashMultiMap<L, R> {
private Map<ObjectHolder, Set<ObjectHolder>> storage;
public SimpleBiMultiMap() {
storage = new HashMap<ObjectHolder, Set<ObjectHolder>>();
}
public void put(L left, R right) {
ObjectHolder leftObjectHolder = ObjectHolder.newLeftHolder(left);
ObjectHolder rightObjectHolder = ObjectHolder.newRightHolder(right);
put(leftObjectHolder, rightObjectHolder);
put(rightObjectHolder, leftObjectHolder);
}
private void put(ObjectHolder key, ObjectHolder value) {
if (!storage.containsKey(key)) {
storage.put(key, new HashSet<ObjectHolder>());
}
storage.get(key).add(value);
}
public Set<R> getRight(L left) {
return this.get(ObjectHolder.newLeftHolder(left));
}
public Set<L> getLeft(R right) {
return this.get(ObjectHolder.newRightHolder(right));
}
private <V> Set<V> get(ObjectHolder key) {
Set<ObjectHolder> values = storage.get(key);
if (values == null || values.isEmpty()) {
return null;
}
Set<V> result = new HashSet<V>();
for (ObjectHolder value : values) {
result.add((V)value.getObject());
}
return result;
}
}
看起来很奇怪的是到处都是left
和right
前缀变量。您可以将它们视为left
是原始密钥,它是用于映射的,而right
是值。
使用示例:
BiHashMultiMap<Integer, Integer> multiMap = new BiHashMultiMap<Integer, Integer>();
multiMap.put(0,5);
multiMap.put(1,10);
multiMap.put(2,7);
multiMap.put(3,7);
multiMap.put(2,8);
multiMap.put(3,11);
Set<Integer> left10 = multiMap.getLeft(10); // [1]
Set<Integer> left7 = multiMap.getLeft(7); // [2, 3]
Set<Integer> right0 = multiMap.getRight(0); // [5]
Set<Integer> right3 = multiMap.getRight(3); // [7, 11]
因此要获得left
值,我们需要提供right
值作为关键,并获得right
值,我们需要提供left
作为关键字。
当然,要使地图完全发挥作用,我们需要提供其他方法,例如remove()
,contains()
等。