【问题标题】:One-to-one mapping data structure (A,B) with getKey(B) in O(1)?O(1)中的getKey(B)一对一映射数据结构(A,B)?
【发布时间】:2012-06-25 03:28:27
【问题描述】:

这个问题最初措辞错误,请参阅下面的编辑。我会把它留给上下文。

我一直在思考构建双射(即一对一)映射的聪明方法。映射函数 A->B(多对一)基本上是 HashMap(A,B) 所做的。如果我现在想要一个数据结构在 O(1) 中实现与 contains() 一对一的东西,我可以使用 java 标准库中的东西吗?请注意,我现在什么都不需要这个,这只是我最近想到的,无法想出一个数据结构,所以答案并不着急。有这样的课吗?如果不是,您认为这是什么原因?

我在 SO 上只能找到关于休眠的东西,这对我没有帮助。

编辑: 我的问题措辞不当,因此需要做出一些解释。

我的意思是“向后”映射 B->A。 HashMap(A,B) 在 O(1) 中包含 contains(A) 和 contains(B),所以这甚至不是我的意思,抱歉造成混淆。我的意思是,是否有一个数据结构映射 AB 在 O(1) 中具有 getValue(A) 和 getKey(B)?

我意识到这可以通过两个 HashMap (A,B) 和 (B,A) 来完成,它们被维护为包含相同的关系,但我认为应该有一个数据结构来处理它而不必这样做“手动”。

【问题讨论】:

  • 只扩展类 A 并添加一个返回 B 的属性对你有用吗?
  • 那么你想要 HashMap 没有做什么呢?它可以用于一对一的映射。
  • @PeterLawrey 是 java O(1) 中的 HashMap.contains 吗?
  • 您是否正在寻找一对一映射的约束,因为 HashMap 可以用于一对一并且具有足够好的散列函数,并且足够小的集合将具有包含在摊销 O(1) 中。
  • 我刚刚意识到我的问题并没有真正带来我想要的;编辑。

标签: java contains one-to-one


【解决方案1】:

我有同样的需求,想出了这个可能有用的 BiMap。它只使用一个 hashmap 并返回一个 entry 对象,这样返回值就可以被赋予一个特定的类型,并允许映射到 null。

public class BiMap<T1, T2>
{
    public static class Entry<T1, T2>
    {
        public final T1 item1;
        public final T2 item2;

        public Entry( T1 item1, T2 item2 )
        {
            this.item1 = item1;
            this.item2 = item2;
        }
    }

    private final HashMap< Object, Entry<T1, T2> > mappings = new HashMap<>();
    private int loopedLinkCount;

    public void put( T1 item1, T2 item2 )
    {
        remove(item1);
        remove(item2);
        Entry<T1, T2> entry = new Entry<T1, T2>( item1, item2 );
        mappings.put( item1, entry );
        mappings.put( item2, entry );
        if( Objects.equals(item1, item2) ){
            loopedLinkCount++;
        }
    }

    /**
     * 
     * @param key 
     * @return an entry containing the key and it's one to one mapping or null if there is
     * no mapping for the key.
     */
    public Entry<T1, T2> get( Object key )
    {
        return mappings.get( key );
    }

    public Entry<T1, T2> remove( Object key )
    {
        Entry<T1, T2> entry = mappings.remove( key );
        if( entry == null ){
            return null;
        }
        if( Objects.equals( entry.item2, entry.item1 ) ){
            loopedLinkCount--;
            return entry;
        }
        return mappings.remove( Objects.equals( key, entry.item1 )  ?  entry.item2  :  entry.item1 );
    }

    public Set< Entry<T1, T2> > entrySet()
    {
        return new HashSet<>( mappings.values() );
    }

    public int size()
    {
        return ( mappings.size() + loopedLinkCount ) / 2;
    }
}

【讨论】:

    【解决方案2】:

    如果您愿意使用第三方库,Guava 为此提供了一个不错的 API,即 BiMap。它提供了一个返回BiMap&lt;V, K&gt;inverse() 视图,而不是getKey 方法。 (当然,它确实提供了恒定时间containsValue。)

    目前,HashBiMap 基本上在内部是两个 HashMaps 保持同步 - 尽管它如何保持它们相互匹配非常一致 - 但未来的实现可能会变得更加智能。

    【讨论】:

    • 关于第三方库的话题,Apache有BidiMap
    【解决方案3】:

    我认为你不会比两个 HashMap 做得更好。编写包装器接口非常简单:

    class OneToOneMap<Key, Value> {
    
        public void add(Key k, Value v) {
            if (!keyToVal_.contains(k) && !valToKey_.contains(v)) {
                keyToVal_.add(k, v);
                valToKey_.add(v, k);
            }
        }
    
        private HashMap<K, V> keyToVal_;
        private HashMap<V, K> valToKey_;
    }
    

    我不确定这是否是有效的 Java,但你明白了。

    【讨论】:

      【解决方案4】:

      我不知道现有的类对 containsKey 和 containsValue 执行 O(1),但您可以通过扩展 HashMap 来实现,以便在插入时将每个值添加到内部 HashSet。重载 containsValue 以查找值 HashSet。标准的 HashMap 有 O(1) containsKey,但 O(n) containsValue。

      同样,您可以在插入中强制执行 1:1 并检查现有值。

      请注意,如果您遇到大量冲突,则 HashSet 查找在最坏的情况下可能会达到 O(n)。

      【讨论】:

      • 很好,所以标准库中可能还没有一个类可以做到这一点。在考虑如何实现它的同时,我也沿着同样的思路思考。
      • 谁能帮我发布一些示例代码
      • 我知道这已经 9 岁了,但我自己只是进行了这次冒险,并认为我会分享我的解决方案,因为它确实需要一些工作:pastebin.com/Wmks9Hut
      【解决方案5】:

      我最初的想法只是使用标准映射,如果您有完美的哈希函数,您可以使用 HashMap 作为一对一映射。如果我了解您在做什么,以下就足够了:

      Map<String,Object> map = new HashMap<String,Object>();  
      

      【讨论】:

      • 这不限于一对一的映射。许多键可以具有相同的值。编辑:我错了……
      • @BlackVegetable 不正确。如果您阅读了我回复的确切措辞,我说的是完美的哈希函数。
      • 对不起。我读得不够仔细。你完全正确!
      • +1 因为它回答了我最初的问题,正如我意识到的那样,这并不是我真正想问的意思。但是,是的,一个完美的散列函数将保证键和值之间的一对一关系,谢谢!
      • 很确定两个 HashMap 可以满足 O(1) 访问映射 A->B 和另一个 B->A 的要求。当然,更新会花费更多,而且您需要双倍的空间,但两次访问都是 O(1)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-10
      • 1970-01-01
      • 2015-12-08
      • 2021-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多