Scala可变HashSet迭代删元素是否安全?自定义dumbRetain能否免并发异常?
关于Scala可变HashSet迭代与自定义retain方法的问题解答
问题1:迭代collection.mutable.HashSet时移除元素是否安全?
答案是不安全。Scala的collection.mutable.HashSet底层依赖Java的哈希表实现,它的迭代器采用fail-fast设计:迭代器初始化时会记录集合的修改计数器,每次调用next()都会检查当前集合的修改计数是否和迭代器保存的一致。如果在迭代过程中直接调用集合的remove()方法(而非迭代器自身的remove()),就会触发计数器不匹配,抛出ConcurrentModificationException。
举个典型的反例:
import scala.collection.mutable.HashSet val set = HashSet(1,2,3,4) for (elem <- set) { if (elem % 2 == 0) set.remove(elem) // 大概率会抛出ConcurrentModificationException }
只有使用迭代器自身的remove()方法才是安全的:
val iter = set.iterator while (iter.hasNext) { val elem = iter.next() if (elem % 2 == 0) iter.remove() // 符合迭代器安全操作规范 }
问题2:自定义dumbRetain方法未触发异常,是否可以放心使用?
你当前没碰到异常只是偶然行为,绝对不能依赖这种“无异常”的情况。
虽然你提到SI-7269的问题源于JavaConversions包装的Java集合,但Scala原生的mutable.HashSet本身就带有fail-fast的检查机制。你没触发异常可能是这些场景:
- 你移除的元素是迭代器已经遍历过的,此时迭代器后续的
next()不会检测到这个修改; - 集合规模很小,修改操作的时机刚好避开了迭代器的检查逻辑;
- 测试用例覆盖不全,没有触发计数器不匹配的场景。
一旦场景变化(比如集合增大、移除的是未遍历的元素),ConcurrentModificationException一定会找上门。
安全实现retain逻辑的建议
- 优先使用Scala内置的
retain方法,它是官方实现,已经处理了所有安全迭代的细节; - 如果要自己实现,要么用迭代器的
remove()方法过滤元素,要么先收集所有需要保留的元素,再重建集合:
def safeRetain(set: mutable.HashSet[Int], predicate: Int => Boolean): Unit = { val toKeep = set.filter(predicate).toSet set.clear() set ++= toKeep }
内容的提问来源于stack exchange,提问作者Dylan
相关产品推荐
相关产品推荐

