Изменяемые поля для объектов в наборе Java

Правильно ли я предполагаю, что если у вас есть объект, который содержится внутри Java Set < > (или как ключ в Map < > для этого), любые поля, которые используются для определения личности или отношения (через hashCode(), equals(), compareTo() и т.д.) нельзя изменить, не вызывая неуказанного поведения для операций над коллекцией? (отредактируйте: как указано в этот другой вопрос)

(Другими словами, эти поля должны быть либо неизменными, либо вам нужно будет удалить объект из коллекции, а затем изменить, а затем снова вставить.)

Я прошу, что я читал справочное руководство Hibernate Annotations, и в нем есть пример, где есть HashSet<Toy>, но класс Toy имеет поля name и serial, которые изменяются и также используются в расчете hashCode()... красный флаг ушел в моей голове, и я просто хотел убедиться, что понял последствия он.

Ответ 1

В javadoc для Set говорится

Примечание. Следует проявлять большую осторожность, если изменяемые объекты используются как установленные элементы. Поведение набора не является если значение объекта равно изменен таким образом, который затрагивает равняется сравнению, пока объект элемент в множестве. Специальный случай этого запрета заключается в том, что это не допустимо для набора, содержащего как элемент.

Это просто означает, что вы можете использовать изменяемые объекты в наборе и даже изменять их. Вы просто должны убедиться, что изменение не влияет на то, как Set находит элементы. Для HashSet, для чего не требуется изменять поля, используемые для вычисления hashCode().

Ответ 2

Это правильно, это может вызвать некоторые проблемы с поиском записи в карте. Официально поведение undefined, поэтому, если вы добавите его в hashset или в качестве ключа в hashmap, вы не должны его изменять.

Ответ 3

Да, это может привести к плохим вещам.

// Given that the Toy class has a mutable field called 'name' which is used
// in equals() and hashCode():
Set<Toy> toys = new HashSet<Toy>();
Toy toy = new Toy("Fire engine", ToyType.WHEELED_VEHICLE, Color.RED);
toys.add(toy);
System.out.println(toys.contains(toy)); // true
toy.setName("Fast truck");
System.out.println(toys.contains(toy)); // false

Ответ 4

В HashSet/HashMap вы можете мутировать содержащийся объект для изменения результатов операции compareTo() - относительное сравнение не используется для поиска объектов. Но это было бы фатально в TreeSet/TreeMap.

Вы также можете мутировать объекты, находящиеся внутри IdentityHashMap, для определения содержимого используется не что иное, как идентификатор объекта.

Несмотря на то, что вы можете делать эти вещи с такой квалификацией, они делают ваш код более хрупким. Что, если кто-то захочет перейти на TreeSet позже или добавить это изменяемое поле в тест hashCode/равенство?