在迭代期间从集合中删除

时间:2013-05-24 08:16:45

标签: java

我有一组具有connection方法的send个对象(我无法更改的库代码)。如果发送失败,他们会回调我在我的代码中调用onClosed的通用removeConnection()侦听器,这将从集合中删除连接。

onClosed回调是通用的,可以随时调用。例如,当对等体关闭连接时调用它,而不仅仅是在写入失败时调用。

但是 ,如果我有一些代码循环我的连接并发送,那么onClosed回调将尝试在迭代期间修改集合。 / p>

我的当前代码在每次迭代之前创建连接列表的副本;然而,在剖析中,这显示出非常昂贵。

Set<Connection> connections = new ....;

public void addConnection(Connection conn) {
    connections.add(conn);
    conn.addClosedListener(this);
}

@Override void onClosed(Connection conn) {
    connections.remove(conn);
}

void send(Message msg) {
    // how to make this so that the onClosed callback can be safely invoked, and efficient?
    for(Connection conn: connections)
        conn.send(msg);
}

如何在迭代过程中有效地处理修改集合?

4 个答案:

答案 0 :(得分:3)

使用List Iterator使用并发修改迭代集合而没有任何例外。

http://www.mkyong.com/java/how-do-loop-iterate-a-list-in-java/ - 示例

如果您使用简单的forforeach循环,则会在删除元素期间收到ConcurrentModificationException - 请注意。

另外,您可以使用自己的List Iterator覆盖java.util.Iterator并添加所需的逻辑。只需实现{{1}}界面。

答案 1 :(得分:1)

我会写一个集合包装:

  1. 保留一组要删除的对象。如果跨越底层集合的迭代遇到其中一个,则跳过它。
  2. 完成迭代后,在列表中进行第二次传递以删除所有收集的对象。
  3. 也许是这样的:

    class ModifiableIterator<T> implements Iterator<T> {
      // My iterable.
      final Iterable<T> it;
      // The Iterator we are walking.
      final Iterator<T> i;
      // The removed objects.
      Set<T> removed = new HashSet<T>();
      // The next actual one to return.
      T next = null;
    
      public ModifiableIterator(Iterable<T> it) {
        this.it = it;
        i = it.iterator();
      }
    
      @Override
      public boolean hasNext() {
        while ( next == null && i.hasNext() ) {
          // Pull a new one.
          next = i.next();
          if ( removed.contains(next)) {
            // Not that one.
            next = null;
          }
        }
        if ( next == null ) {
          // Finished! Close.
          close();
        }
        return next != null;
      }
    
      @Override
      public T next() {
        T n = next;
        next = null;
        return n;
      }
    
      // Close down - remove all removed.
      public void close () {
        if ( !removed.isEmpty() ) {
          Iterator<T> i = it.iterator();
    
          while ( i.hasNext() ) {
            if ( removed.contains(i.next())) {
              i.remove();
            }
          }
          // Clear down.
          removed.clear();
        }
      }
    
      @Override
      public void remove() {
        throw new UnsupportedOperationException("Not supported.");
      }
    
      public void remove(T t) {
        removed.add(t);
      }
    
    }
    
    public void test() {
      List<String> test = new ArrayList(Arrays.asList("A","B","C","D","E"));
      ModifiableIterator i = new ModifiableIterator(test);
      i.remove("A");
      i.remove("E");
      System.out.println(test);
      while ( i.hasNext() ) {
        System.out.println(i.next());
      }
      System.out.println(test);
    }
    

    您可能需要考虑您的列表是否可以包含空值,在这种情况下,您需要稍微调整一下。

    如果在完成之前放弃迭代,请记住close迭代器。

答案 2 :(得分:1)

ConcurrentSkipListSet可能就是你想要的。

您还可以使用CopyOnWriteArraySet。这当然仍然会复制,但是,只有在修改集合时才会复制。因此,只要不定期添加或删除Connection个对象,就会更有效。

答案 3 :(得分:1)

您也可以使用ConcurrentHashMap。 ConcurrentHashMap是线程安全的,因此您无需进行复制即可进行迭代。 看看这个实现.. http://www.java2s.com/Tutorial/Java/0140__Collections/Concurrentset.htm