我有一组具有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);
}
如何在迭代过程中有效地处理修改集合?
答案 0 :(得分:3)
使用List Iterator
使用并发修改迭代集合而没有任何例外。
http://www.mkyong.com/java/how-do-loop-iterate-a-list-in-java/ - 示例
如果您使用简单的for
或foreach
循环,则会在删除元素期间收到ConcurrentModificationException
- 请注意。
另外,您可以使用自己的List Iterator
覆盖java.util.Iterator
并添加所需的逻辑。只需实现{{1}}界面。
答案 1 :(得分:1)
我会写一个集合包装:
也许是这样的:
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