【问题标题】:Java collection to allow adding and removing while iterating允许在迭代时添加和删除的 Java 集合
【发布时间】:2012-06-28 23:37:17
【问题描述】:

如果有任何框架实现了具有以下行为的集合,我很感兴趣。


假设它最初包含:[1, 2, 3]

  • 我迭代它(使用迭代器)并到达元素 2,现在我将 4 添加到末尾(集合现在将是 [1, 2, 3, 4])。
  • 现在我创建一个新的迭代器并迭代集合,得到 [1, 2, 3, 4]
  • 我继续使用第一个迭代器进行迭代,它只会给我 3 个并返回
  • 现在重置第一个迭代器会给我 [1, 2, 3, 4](类似于创建一个新迭代器)。

同样适用于删除元素。如果我删除 3 而不是添加,第二个迭代器应该给我 [1, 2] 而第一个迭代器仍然给我 3 和结尾。


所以当我得到和迭代器时,我希望它给我创建迭代器时拥有的集合(即使我稍后迭代它,我会迭代一点并稍后继续),当我重置迭代器时,它会得到垃圾收集后它将更新到最新版本,我应该能够在不同时间创建多个迭代器实例,这些实例将根据创建迭代器时数组的内容提供不同的版本。

我需要它与多个线程一起工作,并且最好有一个高效的实现。

有谁知道此类集合的任何实现,还是我必须自己实现?

【问题讨论】:

    标签: java collections frameworks concurrent-collections


    【解决方案1】:

    java.util.concurrent.CopyOnWriteArrayList 将像这样运行,除了没有 Java 集合具有“重置”迭代器 - 但是获取新迭代器而不是重置具有您在此处请求的效果。

    【讨论】:

      【解决方案2】:

      您描述的内容与CopyOnWriteArrayList 的工作方式非常相似:

      • 一旦开始迭代,就可以更改集合(包括来自另一个线程)而不影响迭代
      • 如果您创建一个新的迭代器,它将基于创建时的集合
      • 它是线程安全的

      下面的简单示例,输出如下:

      迭代器 1 - 1
      已添加 4 个
      迭代器 2 - 1
      迭代器 2 - 2
      迭代器 2 - 3
      迭代器 2 - 4
      迭代器 1 - 2
      迭代器 1 - 3

      public static void main(String[] args) throws InterruptedException {
          final List<Integer> list = new CopyOnWriteArrayList<Integer>();
          list.addAll(Arrays.asList(1, 2, 3));
          new Thread(new Runnable() {
      
              @Override
              public void run() {
                  for (Integer i : list) {
                      System.out.println("Iterator 1 - " + i);
                      try {
                          Thread.sleep(10);
                      } catch (InterruptedException e) {}
                  }
              }
          }).start();
          Thread.sleep(10);
          list.add(4);
          System.out.println("4 has been added");
          for (Integer i : list) {
              System.out.println("Iterator 2 - " + i);
          }
      
      }
      

      【讨论】:

      • 它是否在更改时(内部)创建集合的新副本?还是以更有效的方式实施?
      • 是的,如 javadoc 中所述:“所有可变操作(添加、设置等)都是通过制作底层数组的新副本来实现的。”
      • 行为看起来像我需要的。但该系列将包含相当多的项目。迭代器经常在其上创建,但我认为更改很少见(因此更改时的副本可能不会导致太多的性能损失)。
      • @Razvi 在这种情况下你应该没问题。如果您获得的性能与您的期望不符,您总能找到一些东西。
      【解决方案3】:

      您可以使用 guava 库中的 ImmutableCollections

      返回的 ImmutableList 经常 -- 不总是,但经常 -- 一个恒定的开销视图,而不是一个显式的副本。也就是说,它通常比你的普通 List 更聪明——例如,它会使用 高效的 contains 支持集合的方法。

      【讨论】:

        【解决方案4】:

        你可以使用java.util.concurrent.CopyOnWriteArrayList&lt;E&gt;

        根据文档:

        ArrayList 的线程安全变体,其中所有可变操作 (添加、设置等)是通过制作新的副本来实现的 底层数组。

        成本高,但线程安全。

        这通常成本太高,但可能比 当遍历操作大大超过突变时的替代方案,以及 当您不能或不想同步遍历时很有用,但是 需要排除并发线程之间的干扰。 “快照” 样式迭代器方法使用对数组状态的引用 迭代器的创建点。

        由于迭代发生在一种快照上,因此不支持对 Iterator 本身的操作(removesetadd)。

        【讨论】:

          【解决方案5】:

          javolution 有线程安全的 FastMap

          【讨论】:

            猜你喜欢
            • 2020-07-27
            • 2011-03-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-13
            • 2016-11-29
            • 2013-06-12
            • 2012-05-13
            相关资源
            最近更新 更多