ArrayList – 添加“相同”对象(same => equals,hashCode),Threads

我有一个问题。 当我尝试将“相同”对象两次添加到ArrayList时会发生什么。 “相同”是指单个类的对象,使用equals()和hashCode()标识为相同。 它对于大多数成员变量具有不同的值,并且可能是从不同的线程创建的,但对于equals()和hashCode(),它们是“相同的”。 第二个对象是否会替换第一个对象?

另外,如果两个线程试图将这些对象完全同时添加到ArrayList会发生什么? 这有可能吗? 如果是,会发生什么?

谢谢! 🙂

[编辑]感谢所有的答案! 我应该使用synchronizedList,而不是使用“synchronize(list){}”吗? – >我读了docs,即使是使用synchronizedList,也应该使用迭代同步(list)

[EDIT2] synchronizedList可以声明为成员变量吗? 我试过了,但它没有用。

不, ArrayList根本不会尝试检测重复项 – 您可以使用具有完全相同引用的ArrayList多次出现。 如果您希望集合避免重复,则需要Set实现 – 如果您还想保留插入顺序,则可能需要LinkedHashSet

但是请注意,没有锁定ArrayList不应该首先从多个线程变异 – 它本身并不意味着是一个线程安全的集合。 几个线程可以在没有同步的情况下从ArrayList 读取 ,但不会改变它。 来自文档:

请注意,此实现不同步。 如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素的任何操作,或显式调整后备数组的大小;仅设置元素的值不是结构修改。)这通常通过同步一些自然封装的对象来实现。名单。 如果不存在此类对象,则应使用Collections.synchronizedList方法“包装”该列表。 这最好在创建时完成,以防止意外地不同步访问列表

如果你想在没有锁定的情况下从多个线程变异集合,我建议你看一下java.util.concurrent的集合。

第二个对象是否会替换第一个对象?

不,大多数开发人员都做了明确的检

 if(!list.contains(foo)){ list.add(foo); } 

另外,如果两个线程试图将这些对象完全同时添加到ArrayList会发生什么? 这有可能吗? 如果是,会发生什么?

是的,这是可能的。 如果多个线程从同一个ArrayList写入/读取,则每次访问此列表时都使用synchronized关键字

 public List getFoos(){ synchronized(list){ return list; } } public void addFoo(Foo foo){ synchronized(list){ list.add(foo); } } 

编辑

正如有人指出的那样,我想检查ArrayList是否包含要添加的对象是非常昂贵的。 如果你想确保只添加一次对象,我会遵循以下使用LinkedHashSet的建议。 根据API,在尝试添加到此数据结构时

如果指定的元素尚不存在,则将其添加到此集合中。 更正式地,如果此集合不包含元素e2(e == null?e2 == null:e.equals(e2)),则将指定元素e添加到此集合。 如果此set已包含该元素,则调用将保持set不变并返回false。

它将允许简单地添加。 List与hashCode()equals()无关,而插入则不关心重复。

ArrayList不是线程安全的,因此您可能无法获得所需的结果。 你可以从Collections类中拥有synchronizedList

ArrayList可以包含对同一个确切对象的多个引用(标识等效)。 添加对象时不检查equals()hashCode()

您最终将在ArrayList中找到两个引用。

ArrayList不是线程安全的…所以如果你试图同时添加两个线程的行为是未定义的。 如果你想做类似的事情,也许尝试使用SynchronizedList

如果你试图两次添加相同的对象,它将工作,或者如果你尝试添加两个对象,但它仍然有效。 这样做不是最佳做法,因为更难维护列表。

总的来说:你不应该这样做