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

興味深いことに、それはバズが変わったことを知っています。ただし、それでもBaz2は見つかりません。

明らかにこれは不自然ですが、私が見ている非常にもっともらしいコードは(複数の間接的な方法で)この問題を引き起こしているようです。Boost Multi Indexでは、少なくともこれを引き起こすにはイテレータをconst-castする必要があります!

4

2 に答える 2

3

Setで(またはのキーとして)可変オブジェクトを使用することはお勧めできませんMap。JavadocがSet言うように:

注:可変オブジェクトをセット要素として使用する場合は、細心の注意を払う必要があります。オブジェクトがセット内の要素であるときに、等しい比較に影響を与える方法でオブジェクトの値が変更された場合、セットの動作は指定されません。

Setしたがって、あなたの例は直接的を射たものであり、あなたを「行動...指定されていない」領域に置きます。

根本的な理由は、PaulBelloraが彼の答えで言っているとおりです。

于 2012-05-28T03:45:49.677 に答える
1

キーのみを気にするaを単にラップするLinkedHashSetextendsに注意してください。したがって、ここで実際に話しているのは、の動作です。HashSetHashMapHashMap

HashMapキーへの参照を保持するだけで、それらのオブジェクトに変更が加えられたときの追跡については責任を負いません。キーのハッシュを計算するのは、キーが存在するとき、putまたはキーのHashMapサイズが変更されたときだけです。

理論的には、キーへの変更を追跡するカスタムHashMap実装が可能ですが、これは、プロパティが変更されたときに通知を発行するインターフェイスを実装するキーに依存します。このカスタムHashMapは特定のタイプのキーでのみ使用できるという事実により、非常に特殊化されます。

于 2012-05-28T03:34:36.127 に答える