假设我有一组整数,我想增加集合中的每个整数.我该怎么做?
我是否允许在迭代时添加和删除集合中的元素?
我是否需要创建一个新的集合,我将"复制和修改"元素,而我正在迭代原始集合?
编辑:如果集合的元素是不可变的怎么办?
Jon*_*ead 90
您可以在迭代期间使用Iterator对象安全地从集合中删除; 尝试在迭代时通过其API修改集合将破坏迭代器.Set类通过getIterator()提供一个迭代器.
但是,Integer对象是不可变的; 我的策略是遍历集合,对于每个Integer i,将i + 1添加到一些新的临时集合中.完成迭代后,从原始集中删除所有元素并添加新临时集的所有元素.
Set<Integer> s; //contains your Integers
...
Set<Integer> temp = new Set<Integer>();
for(Integer i : s)
temp.add(i+1);
s.clear();
s.addAll(temp);
Run Code Online (Sandbox Code Playgroud)
Shi*_*gon 39
如果使用迭代器对象来遍历集合中的元素,则可以执行所需的操作.您可以随时删除它们即可.然而,在for循环中删除它们("标准",每种类型)会让你遇到麻烦:
Set<Integer> set = new TreeSet<Integer>();
set.add(1);
set.add(2);
set.add(3);
//good way:
Iterator<Integer> iterator = set.iterator();
while(iterator.hasNext()) {
Integer setElement = iterator.next();
if(setElement==2) {
iterator.remove();
}
}
//bad way:
for(Integer setElement:set) {
if(setElement==2) {
//might work or might throw exception, Java calls it indefined behaviour:
set.remove(setElement);
}
}
Run Code Online (Sandbox Code Playgroud)
根据@ mrgloom的评论,这里有更多细节,说明为什么上面描述的"坏"方式,好......坏:
在没有深入了解Java如何实现这一点的细节的情况下,我们可以说"糟糕"的方式很糟糕,因为它在Java文档中明确规定:
https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html
除其他外,规定(强调我的):
" 例如,一个线程通常不允许修改Collection而另一个线程正在迭代它.通常,在这些情况下迭代的结果是不确定的.一些Iterator实现(包括所有通用集合的实现)如果检测到这种行为,则JRE提供的实现可以选择抛出此异常"(...)
" 请注意,此异常并不总是表明某个对象已被另一个线程同时修改.如果一个线程发出一系列违反对象契约的方法调用,该对象可能会抛出此异常.例如,如果一个线程在使用失败快速迭代器迭代集合时直接修改集合,迭代器将抛出此异常."
更详细地说:可以在forEach循环中使用的对象需要实现"java.lang.Iterable"接口(这里是 javadoc ).这将生成一个Iterator(通过此接口中的"Iterator"方法),它按需实例化,并在内部包含对创建它的Iterable对象的引用.但是,当在forEach循环中使用Iterable对象时,此迭代器的实例对用户是隐藏的(您无法以任何方式自行访问它).
这与迭代器非常有状态这一事实相结合,即为了实现其魔力并对其"next"和"hasNext"方法具有连贯的响应,它需要支持对象不会被迭代器本身改变.当它迭代时,使它一旦检测到后台对象在迭代它时发生了某些变化就会抛出异常.
Java称之为"快速失败"迭代:即有一些操作,通常是那些修改Iterable实例的操作(当迭代器迭代它时)."失败快速"概念的"失败"部分是指迭代器检测何时发生此类"失败"操作的能力.的"快速失败"(和,这在我看来应该叫"尽力而为,快"),将终止通过ConcurrentModificationException的迭代的"快"的一部分,尽快,因为它可以检测到一个"失败"的动作有发生.
归档时间: |
|
查看次数: |
271646 次 |
最近记录: |