2

我一直在将大量 Java 代码移植到 C++ 中,并且在我离开时不得不实现 LinkedHashSet 之类的东西。我使用 Boost 的多索引容器对 LinkedHashSet/Map 进行了合理的复制。

当我移植代码时,我遇到了一些有趣的东西,其中包含 multi_index,因为包含的对象不是可变的(除非您将类的特定字段标记为可变)。但是,如果密钥是从包含的类的一些可变成员中计算出来的,那么事情就会变得有趣。

为了澄清一些事情,我想我会用 Java 写一个简单的例子来检查他们的 LinkedHashSet 的行为。结果让我有点惊讶;看起来它们的行为类似于 Boost 的多索引容器,因为在修改包含的对象时不会重新生成索引(如您所料);然而,编译器并没有以任何方式抱怨——似乎很容易让自己陷入困境(我正在移植的代码似乎犯了上述罪过,谁知道它仍然是如何工作的)。

这只是 Java 中缺少 const_iterators 的限制,还是我设法做了一些特别愚蠢或棘手的事情?

这是一个简单的例子:

class StringContainer                                                        
{                                                                            
    public String s;                                                         

    public StringContainer(String s)                                         
    {                                                                        
        this.s = s;                                                          
    }                                                                        

    public boolean equals(Object t1)                                         
    {                                                                        
        StringContainer other = (StringContainer) t1;                        
        return this.s == other.s;                                            
    }                                                                        

    public int hashCode()                                                    
    {                                                                        
        int val = 8;                                                         
        for (int i = 0; i < s.length(); i++)                                 
            val += s.charAt(i);                                              
        return val;                                                          
    }                                                                        

    public String toString()                                                 
    {                                                                        
        return s;                                                            
    }                                                                        
}                                                                            

class test                                                                   
{                                                                            
    public static void main(String[] args)                                   
    {                                                                        
        Set<StringContainer> set = new LinkedHashSet();                      
        set.add(new StringContainer("Foo"));                                 
        set.add(new StringContainer("Bar"));                                 
        set.add(new StringContainer("Baz"));                                 
        set.add(new StringContainer("Qux"));                                 


        Iterator<StringContainer> it = set.iterator();                       
        while (it.hasNext())                                                 
        {                                                                    
            StringContainer s = it.next();                                   
            if (s.s == "Baz")                                                
                s.s = "Baz2";                                                
            System.out.println(s);                                           
        }                                                                    

        System.out.println("\nRe-iterate:\n");                               

        it = set.iterator();                                                 
        while (it.hasNext())                                                 
        {                                                                    
            StringContainer s = it.next();                                   
            System.out.println(s);                                           
        }                                                                    

        System.out.println();                                                

        if (set.contains(new StringContainer("Foo")))                        
            System.out.println("Contains Foo");                              

        if (set.contains(new StringContainer("Baz")))                        
            System.out.println("Contains Baz");                              
        else                                                                 
            System.out.println("Does not contain Baz");                      

        if (set.contains(new StringContainer("Baz2")))                       
            System.out.println("Contains Baz2");                             
        else                                                                 
            System.out.println("Does not contain Baz2");                     
    }                                                                        
}

它打印出以下内容:

Foo
Bar
Baz2
Qux

Re-iterate:

Foo
Bar
Baz2
Qux

Contains Foo
Does not contain Baz
Does not contain Baz2

有趣的是,它知道 Baz 变了;但是它仍然没有找到 Baz2。

显然这是人为的,但我正在查看的非常合理的代码似乎(通过多个间接)导致了这个问题。使用 Boost Multi Index 至少你必须 const-cast 一个迭代器来导致这个!

4

2 回答 2

3

不建议在中使用可变对象Set(或作为键Map)。正如 JavadocSet所说:

注意:如果将可变对象用作集合元素,则必须非常小心。如果对象的值以影响等于比较的方式更改,而对象是集合中的一个元素,则不指定集合的​​行为。

所以你的例子是直接的,并将你Set放在“行为......未指定”区域。

根本原因正如 Paul Bellora 在他的回答中所说的那样。

于 2012-05-28T03:45:49.677 回答
1

请注意,LinkedHashSetextendsHashSet只是简单地包装了HashMap只关心其键的 a。所以我们在这里真正谈论的是HashMap.

HashMap仅保存对其键的引用,并且不负责跟踪何时对这些对象进行更改。它计算键的哈希值的唯一时间是它们是什么时候putHashMap调整本身大小的时候。

理论上,一个自定义 HashMap 实现可以跟踪其键的更改,但这取决于键实现了一个接口,该接口在其属性更改时触发通知。这个自定义 HashMap 只能与特定类型的键一起使用这一事实将使其非常专业化。

于 2012-05-28T03:34:36.127 回答