【问题标题】:Scala mutable set: strange behaviorScala可变集:奇怪的行为
【发布时间】: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。

标签: scala set


【解决方案1】:

当您更改content 时,Item 的哈希码会发生变化。由于Set(...) 创建的集合是一个散列集,如果其元素的散列发生变化,它就不能正常工作。请注意,Set[Item] 是否可变并不重要;只有content 是可变的。

如果您将可变对象放入哈希集中或将它们用作哈希映射的键,您必须确保 1) 它们在此期间不会发生变异或 2) 它们的 @987654326 @方法是稳定的。

【讨论】:

  • 刚刚检查过了。如果你用即 name.size 覆盖哈希码,它会起作用。
  • @goral name.hashCode 会好一点。
  • 试过hashcode,难怪没用。无论哪种方式,我只是想检查一下:)
  • 我确信每次都会重新计算相等和哈希值。嗯,这就解释了。另外,case 类直到最近才拥有自己的集合,所以我没有这样计划,它只是发生了。从好的方面来说,它确实教会了我更多关于 Scala 的知识。谢谢。
  • “我确信每次都重新计算相等和哈希值。”是的,他们是。这正是问题所在:该元素仍存储在其旧哈希值下,但通过当前哈希值查找。
猜你喜欢
  • 2023-03-31
  • 2011-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-02
  • 1970-01-01
相关资源
最近更新 更多