如何在Java中迭代HashSet时覆盖元素

3

在迭代HashSet时,是否可能覆盖一些元素(不一定是正在迭代的元素)。我想知道除了删除、编辑和再添加之外的方法。

我之所以这样问,是因为在迭代时使用remove总是会导致java.util.ConcurrentModificationException。我发现没有一个方法可以覆盖集合中的元素。

任何帮助都将不胜感激。

谢谢,

Somnath


违反HashSet合同的规定,修改参与hashCode()或equals()方法的Set成员的部分是不允许的。一般来说,我建议始终删除旧元素并插入新元素。 - Jim
但是在迭代时删除会导致异常。 - somnathchakrabarti
请参见以下内容以了解在迭代时如何删除。 - Jim
4个回答

4
您可以在使用Iterator迭代并调用Itertator.remove()来删除其中一个项,包括Set的项,但这仅允许您删除迭代中的当前项。
您不能删除另一项或添加另一项,否则会导致ConcurrentModificationException,因为如果更改Set,不清楚如何进行迭代。
此外,与List不同,谈论在Set中替换条目并不完全合理。在List中,条目具有定义的顺序,因此,例如,如果第二个条目是"A",则可以将其替换为"B"。 Set中的项没有顺序,因此无法直接用另一个替换其中一个,您只能删除旧的并添加新的。

根据您想要做的事情而定,最好的方法可能是循环一个Set的拷贝:

Set<Object> originalSet = someMethod(); 

for (Object item : new HashSet<Object>(originalSet)) {
  //do stuff which modifies originalSet
}

然而,您必须考虑到迭代的对象将是原始值,并且不会反映您所做的任何更改。
如果这样做不行,那么可能有意义找到另一种处理 Set 中的项的方法,而不仅仅是通过跟踪已处理的节点来迭代它们。
像这样的东西可能可以做到,但根据您所做的事情可能还可以改进:
Set<Object> originalSet = somemethod();

//Keep track of items we've already processed
Set<Object> processed = new HashSet<Object>();

//toDo is used to calculate which items in originalSet aren't in processed
Set<Object> toDo = new HashSet(originalSet);
toDo.removeAll(processed);

while (! toDo.isEmpty()) {
  //Get an object from toDo
  Object item = toDo.iterator().next();

  //Do some processing
  //Can update originalSet or even remove from processed if we want to re-do an item

  //Recalculate what's left to do
  processed.add(item);
  toDo = new HashSet(originalSet);
  toDo.removeAll(processed);
}

我正在使用一组图节点,这些节点具有ID和属性“连接组件编号”。当我迭代图节点并在迭代节点的邻接列表中找到某个节点时,我需要更新连接的组件编号。因此,显然似乎我需要更新正在迭代的集合。但是我将尝试看看是否可以在原始集合的副本上完成。 - somnathchakrabarti

2
Set<Key> set = Sets.newHashSet();
// ...

for (Key k : ImmutableSet.copyOf(set)) {
  if (needToMessWith(k)) {
    set.remove(k);
    k.mutateAsNeeded();
    set.add(k);
  }
}

// 我使用了Guava集合代码。但是这个操作也可以很简单地完成。


但调用remove将会导致java.util.ConcurrentModificationException异常。这就是我的意思。 - somnathchakrabarti
抱歉,我添加了 copyOf() 来进行浅拷贝集合。 - Jim

1

只需将set转换为List并使用{{link1:set()}},完成后将其转换回Set

或者在迭代时维护另一个Set


我的需求是在图中遍历一组节点,并在发现相邻节点时更新节点属性 - 连通分量编号,并在遍历时修改该集合。 - somnathchakrabarti

1

无法实现。因为替换元素基本上是删除+插入。 这是一个集合,而不是列表。例如,集合{1,2}中有两个元素,您想用{2}替换{1}。这是不可能的,因为结果集不能包含{2,2} - 即两个相等的元素。


如果集合是{1,2},我想在迭代1时更新2。我认为1、2是具有属性的对象。 - somnathchakrabarti
1
我建议您保留两个集合:removed和added。在迭代过程中,将要移除的元素添加到第一个集合中,将新条目添加到后者中。在迭代块之后,分别使用“removeAll”和“addAll”进行操作。 - Eugene Retunsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接