【问题标题】:Bidirectional multimap equivalent data structure双向多图等效数据结构
【发布时间】:2014-07-20 12:12:05
【问题描述】:

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

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

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

我希望能够get(7) -> 返回2get(2) 返回[7, 8]。 是否有另一个库具有我可以使用的数据结构?

如果不是,您认为处理这种情况的更好选择是什么?将两个Multimaps 保留在内存中,一个与另一个具有不好的做法吗?

P.S.:我读过这个问题:Bidirectional multi-valued map in Java,但考虑到它的日期是 2011 年,我想我会提出一个更新的问题

【问题讨论】:

  • 您期望的数据大小是多少?
  • 伯尼斯,我不喜欢重新发明轮子,但如果 Guava 产品和 Apache 产品都不足以满足您的目的,那么推出自己的产品可能会更省力执行,而不是进一步环顾四周。毕竟,这可以很容易地通过一个HashMap&lt;Integer,Integer&gt;(用于唯一方向)和一个HashMap&lt;Integer, HashSet&lt;Integer&gt;&gt;(用于非唯一方向)来完成。
  • 从 2 个 Multimaps 包裹在您自己的 BiMultimap 中开始,并验证它是否足够快速和紧凑以满足您的需求(它是仅附加还是您需要删除条目?多久一次它被实例化了吗?它实际上有多大?)。除非它在某些方面有所欠缺,否则你现在就完成了。
  • 尽管有my answer is from 2011,但从那时起并没有真正改变。如果你有不可变的结构,我仍然推荐ImmutableMultimap#inverse()(在你的情况下solution from comment #15)可能会更好。
  • 另外,请登录、评论和投票给this issue - 25 颗星已经够多了,也许 Guava 图书馆员会重新考虑开源 BiMultimap(显然对它的需求很大)。

标签: java data-structures guava multimap bidirectional


【解决方案1】:

什么意思

Guava 内部有一个 BiMultimap 类,但没有外包代码

code of an implementation is here

我没有检查这是否是一个有效的实现,也没有检查它是否已发布,或者我只是在查看某种快照。一切都是公开的,所以你应该能够得到它。

快速浏览一下源代码,看起来该实现确实维护了两个 MultMap,这对于一般情况应该没问题。

【讨论】:

【解决方案2】:

如果您不需要全部 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() 等。

【讨论】:

    猜你喜欢
    • 2011-08-06
    • 2023-04-08
    • 2012-11-05
    • 1970-01-01
    • 2014-03-21
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多