在O(1)中使用getKey(B)的一对一映射数据结构(A,B)?

时间:2012-06-22 19:25:52

标签: java contains one-to-one

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

我一直在考虑建立双向(即一对一)映射的智能方法。映射函数A-> B(多对一)基本上是HashMap(A,B)所做的。如果我现在想要一个与O(1)中的contains()实现一对一的数据结构,那么我可以使用java标准库中的某些东西吗?请注意,我现在不需要这个,这只是我最近想到的,无法提供数据结构,所以答案并不急。有类似的课吗?如果没有,您认为为什么会这样?

所有我能找到的就是关于冬眠的事情,这对我没有帮助。

编辑: 我的问题是措辞不好,所以应该作出一些解释。

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

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

5 个答案:

答案 0 :(得分:13)

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

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,但你明白了。

答案 1 :(得分:6)

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

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

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

答案 2 :(得分:4)

如果您愿意使用第三方库,Guava会为BiMap提供一个很好的API。它提供getKey视图,而不是inverse()方法,返回BiMap<V, K>。 (当然,它确实提供了恒定时间containsValue。)

目前,HashBiMap基本上内部有两个HashMap保持同步 - 尽管它们如何保持它们彼此匹配非常一致 - 但实施可能会在未来变得更加智能

答案 3 :(得分:3)

我有同样的需求,想出了可能有用的BiMap。它只使用一个hashmap并返回一个条目对象,以便返回值可以被赋予特定类型,并允许映射为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;
    }
}

答案 4 :(得分:2)

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

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