我有一个问题。当我尝试将“相同”对象两次添加到ArrayList时会发生什么。 “相同”是指单个类的对象,使用equals()和hashCode()标识为相同。它对于大多数成员变量具有不同的值,并且可能是从不同的线程创建的,但对于equals()和hashCode(),它们是“相同的”。 第二个对象是否会替换第一个对象?
此外,如果两个线程试图将这些对象完全同时添加到ArrayList会发生什么?这甚至可能吗?如果是,会发生什么?
谢谢! :-)
[编辑]感谢所有答案!我应该使用synchronizedList,而不是使用“synchronize(list){}”吗? - >我阅读文档,即使使用synchronizedList,也可以使用迭代同步(list)
[EDIT2] 可以将synchronizedList声明为成员变量吗?我试过了,但它没有用。
答案 0 :(得分:15)
不,ArrayList
根本不会尝试检测重复项 - 您可以让ArrayList
具有完全相同的引用多次出现。如果您希望集合避免重复,则需要Set
实现 - 如果 想要保留插入顺序,则可能需要LinkedHashSet
。
但是,请注意,如果没有锁定ArrayList
,首先应该从多个线程中突变 - 它本身并不意味着是一个线程安全的集合。几个线程可以在没有同步的情况下从ArrayList
读取,但不会改变它。来自文档:
请注意,此实现未同步。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素的任何操作,或显式调整后备数组的大小;仅设置元素的值不是结构修改。)这通常通过同步一些自然封装的对象来实现。名单。如果不存在此类对象,则应使用Collections.synchronizedList方法“包装”该列表。这最好在创建时完成,以防止意外地不同步访问列表
如果你想在不锁定的情况下改变多个线程的集合,我建议你看一下java.util.concurrent
中的集合。
答案 1 :(得分:6)
然后替换第二个对象 第一个对象?
不,大多数开发人员都会进行明确的检查
if(!list.contains(foo)){
list.add(foo);
}
此外,如果两个线程尝试会发生什么 准确地添加这些对象 同时到ArrayList?这是 甚至可能?如果是,会发生什么?
是的,这是可能的。如果多个线程写入/读取同一个ArrayList
,则每次访问此列表时都使用synchronized
关键字
public List<Foo> getFoos(){
synchronized(list){
return list;
}
}
public void addFoo(Foo foo){
synchronized(list){
list.add(foo);
}
}
修改强>
有人指出,我想检查ArrayList
是否包含要添加的对象是非常昂贵的。如果您想确保只添加一次对象,我会遵循以下使用LinkedHashSet的建议。根据API,尝试add到此数据结构时
将指定的元素添加到此集合中 如果它还没有出现。更多 正式地,添加指定的元素e 如果此集合不包含此集合 元素e2这样(e == null? e2 == null:e.equals(e2))。如果这个设定 已包含元素,调用 保持设置不变并返回 假的。
答案 2 :(得分:4)
它可以简单地添加。列表与hashCode()
,equals()
无关,而插入则不关心重复。
ArrayList
不是线程安全的,因此您可能无法获得所需的结果。您可以从synchronizedList
班级
Collections
答案 3 :(得分:2)
ArrayList
可以包含对同一个确切对象的多个引用(标识等效)。添加对象时,它不会检查equals()
或hashCode()
。
您最终将在ArrayList中找到两个引用。
ArrayList
不是线程安全的...所以如果你试图同时添加两个线程的行为是未定义的。如果您想做类似的事情,也许可以尝试使用SynchronizedList
。
答案 4 :(得分:1)
如果您尝试将同一个对象添加两次,它将起作用,或者如果您尝试添加两个具有相同内容的对象,它仍然可以工作。这样做不是最佳做法,因为更难维护列表。
总的来说:你不应该这样做