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文档中发表评论?
三江源!
答案 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
。