Java ArrayList-添加“列表”;“相同”;对象(相同=>;等于,哈希代码),线程

Java ArrayList-添加“列表”;“相同”;对象(相同=>;等于,哈希代码),线程,java,multithreading,arraylist,equals,hashcode,Java,Multithreading,Arraylist,Equals,Hashcode,我有一个问题。当我尝试向ArrayList添加两次“相同”对象时会发生什么。“相同”是指单个类的一个对象,它用equals()和hashCode()标识为相同。对于大多数成员变量,它有不同的值,并且可能是从不同的线程创建的,但是对于equals()和hashCode()则是“相同的”。 然后第二个对象是否替换第一个对象 另外,如果两个线程同时尝试将这些对象添加到ArrayList,会发生什么情况?这可能吗?如果是,会发生什么 谢谢!:-) [编辑]谢谢你的回答!我是否应该使用synchroniz

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

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

谢谢!:-)

[编辑]谢谢你的回答!我是否应该使用synchronizedList而不是使用“synchronize(list){}”?-->我阅读了文档,即使使用synchronizedList,也应使用Synchronized(list)进行迭代

[编辑二]
synchronizedList是否可以声明为成员变量?我试过了,但没用

否,
ArrayList
根本不尝试检测重复项-您可以在一个
ArrayList
中多次出现完全相同的引用。如果希望集合避免重复,则需要一个
Set
实现—如果还希望保留插入顺序,则可能需要

但是,请注意,如果不锁定
ArrayList
,那么首先就不应该从多个线程中进行变异,因为这样做并不意味着它是线程安全的集合。多个线程可以从
ArrayList
读取数据,而无需同步,但不能对其进行变异。从文档中:

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


如果您想在不锁定的情况下从多个线程中变异集合,我建议您查看中的集合。

它允许简单地添加。列表与
hashCode()
equals()
无关,但插入时不考虑重复


ArrayList
不是线程安全的,因此可能无法获得所需的结果。您可以从
Collections
class

中获得
synchronizedList
ArrayList
可以包含对同一确切对象的多个引用(标识等价)。添加对象时,它不会选中
equals()
hashCode()

您将在ArrayList中得到两个引用

ArrayList
不是线程安全的…因此,如果您试图同时添加两个线程,则行为是未定义的。如果你想做这样的事情,也许可以试着用一个

然后第二个对象是否替换 第一个对象

不,大多数开发人员都进行显式检查

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);
    }
}
公共列表getFoos(){
已同步(列表){
退货清单;
}
}
公共无效添加Foo(Foo-Foo){
已同步(列表){
添加(foo);
}
}
编辑

正如有人指出的,我认为检查
ArrayList
是否包含要添加的对象是相当昂贵的。如果您想确保只添加一次对象,我将按照下面的建议使用。根据API,当尝试将此数据结构

将指定的元素添加到此集合 如果它还没有出现。更多 形式上,添加指定的元素e 如果此集合不包含任何 元素e2使得(e==null? e2==null:e.equals(e2))。如果这一套 已包含元素,调用 保持集合不变并返回 错


如果您尝试添加同一对象两次,它将起作用,或者如果您尝试添加两个具有相同内容的对象,它仍将起作用。这样做不是最佳做法,因为维护列表比较困难


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

-1对
ArrayList.contains的每次调用都是O(n)。这是一种创建唯一列表的糟糕方法
LinkedHashSet
更好。但是当使用LinkedHashSet访问时,它也必须同步,对吗?那么为什么不使用synchronizedList呢?@nano7,当然它必须是同步的。更改集合并不意味着更改同步策略。你可以自由使用任何你想要的同步数据结构,但是如果你要使用
LinkedHashSet
,我建议你使用
synchronizedSet
。像这样?Set s=Collections.synchronizedSet(新LinkedHashSet());如何声明这种类型的成员变量?其类型是否为“LinkedHashSet()”?然后我是否手动创建synchronizedSet并将其设置为成员变量(使用强制转换)?如果将一个对象添加到列表中一次,则不能再次添加同一对象,因此不需要同步“添加”,不是吗?