如何迭代和修改 Java 集?
假设我有一组整数,并且我想递增该组中的每个整数。我该怎么做?
我可以在迭代时添加和删除集合中的元素吗?
我是否需要创建一个新集合,在迭代原始集合时将元素“复制并修改”到其中?
编辑:如果集合的元素是不可变的怎么办?
Let's say I have a Set of Integers, and I want to increment every Integer in the Set. How would I do this?
Am I allowed to add and remove elements from the set while iterating it?
Would I need to create a new set that I would "copy and modify" the elements into, while I'm iterating the original set?
EDIT: What if the elements of the set are immutable?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您可以在迭代期间使用 Iterator 对象安全地从集合中删除;尝试在迭代时通过 API 修改集合会破坏迭代器。 Set 类通过 getIterator() 提供迭代器。
然而,Integer 对象是不可变的;我的策略是迭代该集合,并为每个整数 i,将 i+1 添加到一些新的临时集合中。完成迭代后,从原始集中删除所有元素并添加新临时集中的所有元素。
You can safely remove from a set during iteration with an Iterator object; attempting to modify a set through its API while iterating will break the iterator. the Set class provides an iterator through getIterator().
however, Integer objects are immutable; my strategy would be to iterate through the set and for each Integer i, add i+1 to some new temporary set. When you are finished iterating, remove all the elements from the original set and add all the elements of the new temporary set.
如果您使用迭代器对象来遍历集合中的元素,您可以做您想做的事情。您可以在旅途中删除它们,没关系。然而,在 for 循环中删除它们(无论是“标准”,还是每种类型)都会给你带来麻烦:
根据 @mrgloom 的评论,这里有更多细节说明为什么上面描述的“坏”方式是这样的。 .. bad :
在不深入了解 Java 如何实现这一点的太多细节的情况下,在较高的层次上,我们可以说“坏”方式是坏的,因为它在 Java 文档中明确规定了:
https://docs.oracle.com/javase/8/docs /api/java/util/ConcurrentModificationException.html
规定,除其他外,(强调我的):
更详细地说:可在 forEach 循环中使用的对象需要实现“java.lang.Iterable”接口(javadoc 此处)。这会生成一个 Iterator (通过在此找到的“Iterator”方法接口),它根据需要实例化,并且内部包含对创建它的 Iterable 对象的引用,但是,当在 forEach 循环中使用 Iterable 对象时,该迭代器的实例对用户(您)是隐藏的。自己无法以任何方式访问它再加
上迭代器是有状态的这一事实,即为了发挥其魔力并对其“next”和“hasNext”方法有一致的响应,它需要支持对象不被其他东西改变。迭代器本身在迭代时,一旦检测到支持对象在迭代时发生更改,就会抛出异常。
Java 称之为“快速失败”迭代:即有一些操作,通常是修改 Iterable 实例的操作(当 Iterator 对其进行迭代时)。 “快速失败”概念的“失败”部分是指迭代器检测此类“失败”操作何时发生的能力。 “fail-fast”的“fast”部分(在我看来应该称为“best-effort-fast”),将通过 ConcurrentModificationException 尽快终止迭代 >可以检测到发生了“失败”操作。
You can do what you want if you use an iterator object to go over the elements in your set. You can remove them on the go an it's ok. However removing them while in a for loop (either "standard", of the for each kind) will get you in trouble:
As per @mrgloom's comment, here are more details as to why the "bad" way described above is, well... bad :
Without getting into too much details about how Java implements this, at a high level, we can say that the "bad" way is bad because it is clearly stipulated as such in the Java docs:
https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html
stipulate, amongst others, that (emphasis mine):
To go more into details: an object that can be used in a forEach loop needs to implement the "java.lang.Iterable" interface (javadoc here). This produces an Iterator (via the "Iterator" method found in this interface), which is instantiated on demand, and will contain internally a reference to the Iterable object from which it was created. However, when an Iterable object is used in a forEach loop, the instance of this iterator is hidden to the user (you cannot access it yourself in any way).
This, coupled with the fact that an Iterator is pretty stateful, i.e. in order to do its magic and have coherent responses for its "next" and "hasNext" methods it needs that the backing object is not changed by something else than the iterator itself while it's iterating, makes it so that it will throw an exception as soon as it detects that something changed in the backing object while it is iterating over it.
Java calls this "fail-fast" iteration: i.e. there are some actions, usually those that modify an Iterable instance (while an Iterator is iterating over it). The "fail" part of the "fail-fast" notion refers to the ability of an Iterator to detect when such "fail" actions happen. The "fast" part of the "fail-fast" (and, which in my opinion should be called "best-effort-fast"), will terminate the iteration via ConcurrentModificationException as soon as it can detect that a "fail" action has happen.
我不太喜欢迭代器的语义,请考虑将其作为一个选项。
由于您发布的内部状态较少,因此也更安全
I don't like very much iterator's semantic, please consider this as an option.
It's also safer as you publish less of your internal state
您可以创建原始 int 的可变包装器并创建一组:
当然,如果您使用 HashSet,您应该在 MutableInteger 中实现 hash, equals 方法,但这超出了本答案的范围。
You could create a mutable wrapper of the primitive int and create a Set of those:
Of course if you are using a HashSet you should implement the hash, equals method in your MutableInteger but that's outside the scope of this answer.
首先,我认为尝试同时做几件事通常是一种不好的做法,我建议您考虑一下您想要实现的目标。
不过,它是一个很好的理论问题,从我收集的信息来看,
java.util.Set
接口的CopyOnWriteArraySet
实现满足了您相当特殊的要求。http://download.oracle.com /javase/1,5.0/docs/api/java/util/concurrent/CopyOnWriteArraySet.html
Firstly, I believe that trying to do several things at once is a bad practice in general and I suggest you think over what you are trying to achieve.
It serves as a good theoretical question though and from what I gather the
CopyOnWriteArraySet
implementation ofjava.util.Set
interface satisfies your rather special requirements.http://download.oracle.com/javase/1,5.0/docs/api/java/util/concurrent/CopyOnWriteArraySet.html