2014-08-28 3 views
3

Я не могу объяснить это поведение наборов Scala.Scala mutable set: странное поведение

Начнем с нескольких определений.

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() 

Фильтр отлично работает. Может быть, я не могу его изменить, потому что это вал.

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())) 

Nope. Что происходит? Я очень смущен.

РЕДАКТИРОВАТЬ, РЕШЕНИЕ:

Используя этот пост StackOverflow, Scala: Ignore case class field for equals/hascode?, я решил это путем изменения способа я объявить класс случай:

case class Item(name: String)(val content: Set[Int]) 

Таким образом, внутренний набор игнорируется для хэш-код и равноценен, но все же доступен как поле.

+0

Никогда не импортируйте 'mutable.Set' только когда-либо импортирует 'mutable', а затем использует' mutable.Set' в области. Это лучшая практика, потому что mutable следует избегать, как чуму, и сделать ее действительно явной для любого читателя, что вы используете изменяемые типы. – samthebest

+0

Я пытался избежать изменчивости, но в какой-то момент это стало необходимым. Другое решение состоит в том, чтобы сделать поле содержимого «var», и это кажется намного хуже. Кроме того, значение всегда изменяется или запрашивается при получении сообщения в конкретном объекте «объект», поэтому, по крайней мере, нет реальной проблемы параллелизма. – eje211

+0

Да, конечно, иногда это хорошо, особенно для оптимизации на низком уровне или для избежания использования рекурсии. Дело в том, что вы должны просто сделать это очень явным, используя 'mutable.Set', abd также и не должен загрязнять пространство имен, поскольку по умолчанию это' immutable.Set' – samthebest

ответ

6

Hashcode Item изменяется при изменении content. Поскольку набор, созданный Set(...), является хэш-множеством, он не может работать корректно, если хэши его элементов изменяются. Обратите внимание, что не имеет значения, изменен ли он Set[Item]; только content является изменяемым.

Если поместить изменяемые объекты в хэш-набор или использовать их в качестве ключей хэш-карты, вы должны убедитесь, что либо 1) они не мутировал в то время как там, или 2) метод их hashCode стабилен.

+0

Только что проверил. Если вы переопределите hashcode с i.e. name.size, он будет работать. – goral

+0

@goral 'name.hashCode' будет немного лучше. –

+0

Пробовал hashcode, неудивительно, что это не сработало. В любом случае я просто хотел его проверить :) – goral

Смежные вопросы