Scala 在迭代期间从collection.mutable.HashSet中删除元素安全吗?

Scala 在迭代期间从collection.mutable.HashSet中删除元素安全吗?,scala,Scala,可变集合的retain方法被实现: def retain(p:A=>Boolean):单位= 对于(elem是的,您可以这样做,只要您确定这是scala的本机HashSet实现,而不是java的包装器……并且要理解,这不是线程安全的,不应该同时使用(原始的HashSet.retain和其他变体也是这样) 更好的是,只要使用不可变的Set.filter,除非你确实有确凿的证据(不仅仅是直觉)证明您的特定案例绝对需要可变容器。这似乎取决于mutable.HashSet的特定实现,API中没有任何东西

可变集合的
retain
方法被实现:

def retain(p:A=>Boolean):单位=

对于(elem是的,您可以这样做,只要您确定这是scala的本机
HashSet
实现,而不是java的包装器……并且要理解,这不是线程安全的,不应该同时使用(原始的
HashSet.retain
和其他变体也是这样)


更好的是,只要使用不可变的
Set.filter
,除非你确实有确凿的证据(不仅仅是直觉)证明您的特定案例绝对需要可变容器。

这似乎取决于
mutable.HashSet
的特定实现,API中没有任何东西可以保证它适用于
mutable.Set
的所有其他实现,即使我们排除了Java集合的所有包装器

用于
-循环的

for (elem <- self) {
  ...
}
本质上,它只是在底层
FlatHashTable
数组中循环,并在每个元素上调用传递的函数
f
。整个
foreach
根本没有任何可以抛出任何内容的行,它不检查并发[脚注-1]根本不需要修改

一个
ConcurrentModificationException
似乎是不那么麻烦的情况:至少,您的程序失败得很快,甚至返回一个指向问题发生所在行的详细堆栈跟踪。如果它只是恶化为未定义的行为而不抛出任何东西,实际上情况会更糟。这将是最糟糕的情况但是,对于标准库中的集合,不应出现这种最坏的情况:

引述:

在scala/scala#5295(合并到2.12.x中)中,我确保删除迭代器最后返回的元素不会导致迭代器出现问题

因此,只要您在文档中明确声明只支持标准库中的集合,在您自己的代码中使用它很可能不会有任何问题。但如果您在公共界面中使用它,这将导致类似于您问题中引用的“SI-7269”的错误

[footnote-1]与“ConcurrentModificationException”中的“并发”相同,而与“并发执行的线程”不同


编辑:我尝试选择不那么模棱两可的公式。非常感谢@Dima的反馈和众多建议。

scala.collection中没有任何内容。
scala.collection.mutable
是线程安全的。这是一个单独的问题(如果您想要线程的可变性,您必须同步)。最初的问题是java的
HashSet
抛出了一个
ConcurrentModificationException
,这仅仅是因为原始集在迭代过程中被同一线程修改。Scala的实现没有这个“特性”。这不是“运气”@Dima您在我的帖子中读到了一些不存在的东西。我从未声称您需要多线程来触发
ConcModExc
。这根本不是重点。这是“纯粹的运气”,因为
mutable.HashSet
恰好是
scala.collection.mutable.Set
的一个实现,它不会引发此异常。如果运气不好,Dylan库的某些用户或Dylan的同事可能早晚会在
scala.collection.mutable.Set的另一个实现上调用
dumpRetain
对并发修改敏感。这一切都可能发生在单线程上下文中。@Dima:也许我对“纯粹运气”的定义与一般的概念有点不同。将参数类型设置为
mutable。设置
,然后说“我知道永远不会有java集合传递给我的算法”就是我所说的这里的“依赖纯粹的运气”。如果已知它将仅用于
scala.collection.mutable.HashSet
,那么应该在函数签名中明确说明。重点是在同一线程中修改迭代器不是“并发的”"。异常名称用词不当,异常本身是java实现的产物。因此,只要OP不同时使用它,也不向其中发送java集,他就可以了。回复:将类型设置为
Set
…那么,
Set
的其他实现就不会抛出异常。这太糟糕了,有些他们中的一个会,但是,我认为,将其作为合同的一部分是很好的…就像“如果
p
null
),这将崩溃。当然,如果集包含null,那么你可能会遇到
NullPointerException
,但是。。。
def dumbRetain[A](self: mutable.Set[A], p: A => Boolean): Unit = 
  for (elem <- self)
    if (!p(elem)) self -= elem

dumbRetain(mutable.HashSet(1,2,3,4,5,6), Set(2,4,6))
// everything is ok
for (elem <- self) {
  ...
}
override def foreach[U](f: A => U) {
    var i = 0
    val len = table.length
    while (i < len) {
      val curEntry = table(i)
      if (curEntry ne null) f(entryToElem(curEntry))
      i += 1
    }
}