【问题标题】:ArrayList - add "same" objects (same => equals, hashCode), ThreadsArrayList - 添加“相同”对象(相同 => 等于、hashCode)、线程
【发布时间】:2011-05-26 12:16:51
【问题描述】:

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

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

谢谢! :-)

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

[编辑2] 可以将同步列表声明为成员变量吗?我试过了,但没有用。

【问题讨论】:

    标签: java multithreading arraylist equals hashcode


    【解决方案1】:

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

    但是,请注意,如果不锁定 ArrayList,首先应该从多个线程中发生变异 - 它根本不意味着以这种方式成为线程安全的集合。多个线程可以在不同步的情况下读取 ArrayList,但不能改变它。来自文档:

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

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

    【讨论】:

      【解决方案2】:

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

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

      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))。如果这一套 已经包含元素,调用 保持集合不变并返回 假的。

      【讨论】:

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

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

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

      【讨论】:

        【解决方案4】:

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

        您最终会在 ArrayList 中得到两个引用。

        ArrayList 不是线程安全的...因此,如果您尝试同时添加两个线程,则行为未定义。如果您想做类似的事情,也许可以尝试使用SynchronizedList

        【讨论】:

          【解决方案5】:

          如果您尝试添加相同的对象两次,它会起作用,或者如果您尝试添加两个相同的对象,它仍然会起作用。这样做不是最佳实践,因为它更难维护列表。

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-04-18
            • 1970-01-01
            • 1970-01-01
            • 2023-03-29
            相关资源
            最近更新 更多