双向多图等效数据结构

时间:2014-07-20 12:12:05

标签: java data-structures guava multimap bidirectional

我知道Guava内部有一个BiMultimap类,但没有外包代码。我需要一个双向的数据结构,即按键和按值查找,也接受重复。

即。像这样:(在我的例子中,值是唯一的,但两个值可以指向相同的键)

0 <-> 5
1 <-> 10
2 <-> 7
2 <-> 8
3 <-> 11

我希望能够get(7) - &gt;返回2get(2)返回[7, 8]。 那里有另一个库,它有一个我可以使用的数据结构吗?

如果没有,你认为处理这种情况的更好选择是什么?是将两个Multimaps保留在记忆中,另一个是不良做法吗?

P.S。:我读过这个问题:Bidirectional multi-valued map in Java但是考虑到它是在2011年,我想我会打开一个更近期的问题

2 个答案:

答案 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;
    }
}

看起来很奇怪的是到处都是leftright前缀变量。您可以将它们视为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()等。