【发布时间】:2014-08-28 04:25:02
【问题描述】:
我无法解释 Scala 集合的这种行为。
让我们从几个定义开始。
import scala.collection.mutable.Set
case class Item(name: String, content: Set[Int])
val items: Set[Item] = Set.empty
我将在我的集合中添加一个项目。
items += Item("name", Set(1, 2, 3))
我会清空我的内心。
items.filter(_.name == "name") foreach (_.content -= 1)
items
// res7: scala.collection.mutable.Set[Item] = Set(Item(name,Set(2, 3)))
到目前为止一切顺利。
items.filter(_.name == "name") foreach (_.content -= 2)
items.filter(_.name == "name") foreach (_.content -= 3)
items
// res12: scala.collection.mutable.Set[Item] = Set(Item(name,Set()))
完美!现在,我真正想做的是删除带有空内部集的条目。
items.retain(_.content.nonEmpty)
items
// res12: scala.collection.mutable.Set[Item] = Set(Item(name,Set()))
没用。也许我做了相反的测试。
items.retain(_.content.isEmpty)
items
// res14: scala.collection.mutable.Set[Item] = Set(Item(name,Set()))
也没有用。也许过滤器不起作用。
items.filter(_.content.nonEmpty)
// res15: scala.collection.mutable.Set[Item] = Set()
过滤器工作正常。也许我无法更改它,因为它是一个 val。
items += Item("name", Set.empty)
items
// res17: scala.collection.mutable.Set[Item] = Set(Item(name,Set()), Item(name,Set()))
我可以改变它。并添加...更多相同?也许他们都不一样。
items += Item("name", Set.empty)
items
// res19: scala.collection.mutable.Set[Item] = Set(Item(name,Set()), Item(name,Set()))
它们并不完全不同。那么我可以删除其中的任何一个吗?
items -= Item("name", Set.empty)
items
// res21: scala.collection.mutable.Set[Item] = Set(Item(name,Set()))
我可以删除一个。我可以删除另一个,即我从一开始就尝试删除的那个吗?
items -= Item("name", Set.empty)
items
// res23: scala.collection.mutable.Set[Item] = Set(Item(name,Set()))
不。发生了什么?我很困惑。
编辑,解决方案:
使用这个 Stackoverflow 帖子 Scala: Ignore case class field for equals/hascode?,我通过更改声明案例类的方式解决了这个问题:
case class Item(name: String)(val content: Set[Int])
通过这种方式,内部集合被忽略了哈希码和等于评估,但仍然可以作为一个字段访问。
【问题讨论】:
-
永远不要导入
mutable.Set,只导入mutable,然后在范围内使用mutable.Set。这是最佳实践,因为应该像瘟疫一样避免使用 mutable,并让任何读者真正明确地知道您使用的是 mutable 类型。 -
我试图避免可变,但它只是在某些时候变得必要。另一种解决方案是将内容字段设为
var,这似乎更糟。此外,只有在收到特定参与者object的消息时才会更改或查询该值,因此至少不存在真正的并发问题。 -
是的,当然有时它很好,特别是对于低级优化或避免使用递归。关键是你应该简单地使用
mutable.Set使它非常明确,而且你不应该污染命名空间,因为默认是immutable.Set -
我今天确实按照你的建议更改了代码,@samthebest。