I can't explain this behavior of Scala sets.
Let's start with a few definitions.
import scala.collection.mutable.Set
case class Item(name: String, content: Set[Int])
val items: Set[Item] = Set.empty
I'll add an item to my set.
items += Item("name", Set(1, 2, 3))
I'll empty my inner set.
items.filter(_.name == "name") foreach (_.content -= 1)
items
// res7: scala.collection.mutable.Set[Item] = Set(Item(name,Set(2, 3)))
So far so good.
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()))
Perfect! Now, what I REALLY want to do is remove the entries with an empty inner set.
items.retain(_.content.nonEmpty)
items
// res12: scala.collection.mutable.Set[Item] = Set(Item(name,Set()))
Didn't work. Maybe I did the opposite test.
items.retain(_.content.isEmpty)
items
// res14: scala.collection.mutable.Set[Item] = Set(Item(name,Set()))
Didn't work either. Maybe the filter doesn't work.
items.filter(_.content.nonEmpty)
// res15: scala.collection.mutable.Set[Item] = Set()
The filter works fine. Maybe I can't change it because it's a val.
items += Item("name", Set.empty)
items
// res17: scala.collection.mutable.Set[Item] = Set(Item(name,Set()), Item(name,Set()))
I CAN change it. And add... more of the same? Maybe they're all different.
items += Item("name", Set.empty)
items
// res19: scala.collection.mutable.Set[Item] = Set(Item(name,Set()), Item(name,Set()))
They're not all different. So can I remove any of them?
items -= Item("name", Set.empty)
items
// res21: scala.collection.mutable.Set[Item] = Set(Item(name,Set()))
I can remove ONE. Can I remove the other, the one I've been trying to remove from the start?
items -= Item("name", Set.empty)
items
// res23: scala.collection.mutable.Set[Item] = Set(Item(name,Set()))
Nope. What's happening? I'm very confused.
EDIT, SOLUTION:
Using this Stackoverflow post, Scala: Ignore case class field for equals/hascode?, I solved this by changing the way I declare the case class:
case class Item(name: String)(val content: Set[Int])
This way, the inner set is disregarded for hashcode and equals evaluations, but still accessible as a field.
Hashcode of Item changes when you change content. Since a set created by Set(...) is a hash set, it can't work correctly if hashes of its elements change. Note that it doesn't matter whether the Set[Item] is mutable or not; only that content is mutable.
If you put mutable objects into a hash set or use them as keys of a hash map, you must make sure that either 1) they aren't mutated while there or 2) their hashCode method is stable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With