LinkedHashSet文档歧义:重新插入

时间:2014-05-10 02:07:20

标签: java linkedhashset

LinkedHashSet的java文档说明了这一点:

请注意,如果将元素重新插入到集合中,则不会影响插入顺序。 (如果s.contains(e)在调用之前立即返回true,则调用s.add(e)时,将元素e重新插入集合中。)

对于最近的项目,我决定使用一个项目在客户端 - 服务器通信中保存一组数据令牌,以便在某些列表视图窗口小部件中显示给用户。我的想法是,我能够廉价地重新插入具有更新数据的元素,并且用户不会感到惊讶,因为订单不会改变。

显然事实并非如此,使用Oracle JRE 1.7.0_55-b13它的运行方式与其他任何Set一样,因为这个简短的测试程序显示:

import java.util.LinkedHashSet;

import static java.util.Arrays.deepToString;

public class LhsStack 
{
    public static class T 
    {
        public T ( int id ) { this.id = id; }

        public final Integer id;
        public String value;

        @Override
        public int hashCode () { return id.hashCode (); }

        @Override
        public boolean equals ( Object obj ) 
        { 
            return obj instanceof T && id.equals ( ((T)obj).id ); 
        }

        @Override
        public String toString () { return id + " => " + value; }
    }

    public static void main ( String [] args )
    {
        LinkedHashSet < T > set = new LinkedHashSet <> ();

        T a = new T ( 1 ),
          b = new T ( 1 ),
          c = new T ( 2 ),
          d = new T ( 3 );

        a.value = "Hello, World";
        b.value = "World, Hello";
        c.value = "Foo";
        d.value = "Bar"; 

        System.out.println ( "a == b: " + a.equals ( b ) );

        if ( set.add ( a ) ) {
            System.out.println ( "Inserted: " + a.value );
        }

        System.out.println ( "set.contains ( a ): " + set.contains ( a ) );
        System.out.println ( "set.contains ( b ): " + set.contains ( b ) );

        set.add ( c ); set.add ( d );

        System.out.println ( "Elements: " + set.size () );
        System.out.println ( deepToString ( set.toArray () ) );

        if ( set.add ( b ) ) {
            System.out.println ( "Re-Inserted: " + b.value );
        }
        else
        {
            System.out.println ( "Removing and Adding: " + b.value );
            set.remove ( b );
            set.add ( b );
        }

        System.out.println ( "Elements: " + set.size () );
        System.out.println ( deepToString ( set.toArray () ) );
    }
}

输出

a == b: true
Inserted: Hello, World
set.contains ( a ): true
set.contains ( b ): true
Elements: 3
[1 => Hello, World, 2 => Foo, 3 => Bar]
Removing and Adding: World, Hello
Elements: 3
[2 => Foo, 3 => Bar, 1 => World, Hello]

我的问题是,因为元素 b 没有被重新插入集合中(即必须将其删除,然后重新添加以更新其值),具有什么意义呢?在java文档中发表评论?

三江源!

1 个答案:

答案 0 :(得分:1)

通常,linkedHashSet.add(elementToAdd)会使elementToAdd成为linkedHashSet的最后一个元素。 Javadoc中评论的重要性在于elementToAdd已经出现在linkedHashSet内,那么linkedHashSet.add(elementToAdd)只会将其保留到位(并且不会将其移至最后)。

对于你想要做的事情,你最好使用LinkedHashMap<Integer, T>。然后,您可以迭代其values()以迭代顺序获取T个实例,并能够更新映射。 (如果需要,可以将LinkedHashMap<Integer, T>包装在某种容器对象中,而不是提供put(Integer, T),而是提供一个add(T)来处理幕后的键映射。事实上,扩展AbstractSet<T>以创建基于LinkedHashMap<Integer, T>的{​​{1}}实现应该非常简单。)


编辑更新的问题:啊,对不起,对不起,我现在明白了你的困惑。以上是对第一句话的目的的解释(“请注意,如果元素重新插入到集合中,则插入顺序受影响”);我没有意识到你误解了第二句话(如果在Set<T>返回e时调用s,则将一个元素s.add(e)重新插入到集s.contains(e)中紧接在调用之前。“)

所以,让我解释一下。第二句仅仅是“重新插入”一词的定义;它没有描述任何行为。如果你用它已经包含的元素调用它的true方法,那句话并不是说LinkedHashSet会做任何叫做“重新插入”的事情。相反,句子说如果你用它已经包含的元素调用它的add方法,那么这个调用叫做“reinsertion”。重新插入的(非)效果是在第一句中解释的,即它不会将元素移动到最后。

add仍然遵守LinkedHashSet.add的要求,该要求指定“如果指定的元素尚未存在,则将其添加到此集合中(可选操作)。[...]如果已设置此项包含元素,调用使集合保持不变并返回Set.add。“

有些JDK类不遵守他们声称要实现的接口的要求,但是当发生这种情况时,它会被称为粗体警告,而不是隐藏在括号内并且从未提及过再次。有关此示例,请参阅the Javadoc of IdentityHashMap