Cody
Cody

Reputation: 4471

Can't remove the element in a mutable set if it is modified in Kotlin

I have an issue when using removeAll(), some objects can't be removed properly.

data class Dog(var name: String)

val dog1 = Dog(name = "dodo")
val dog2 = Dog(name = "mimi")

val set = mutableSetOf<Dog>()
set.add(dog1)
set.add(dog2)

dog1.name = "dodo2"

val beforeSize = set.size // 2

set.removeAll { true }

val afterSize = set.size  // why it is 1!?, I expect it should be 0

The removeAll didn't work as I expected. There is still one element in the mutable set. Any idea?

Upvotes: 9

Views: 2749

Answers (2)

Marko Topolnik
Marko Topolnik

Reputation: 200148

Javadoc of Set:

Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set.

By writing

dog1.name = "dodo2"

you did exactly that, you changed the object in a way that affects equals comparisons. Specifically, by using the construct

set.removeAll { true }

you exercised a code path in LinkedHashSet that visits each element, tests the predicate on it, and then tries to remove it as if by calling set.remove(it). This will cause it to recalculate its hash code, now different from what it was when you inserted it into the set. LinkedHashSet will then look up the corresponding hash bucket and fail to find it there.

Upvotes: 13

s1m0nw1
s1m0nw1

Reputation: 81879

You're changing the object after it has been added to the set. That should be avoided

You can use clear instead:

set.clear()

Or move the mutation of your object:

val dog1 = Dog(name = "dodo")
val dog2 = Dog(name = "mimi")

dog1.name = "dodo2"
val set = mutableSetOf(dog1, dog2)

Upvotes: 2

Related Questions