2009-07-02 2 views
10

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

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

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

ответ

7

документация Javadoc для Set говорит

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

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

3

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

1

Да, это приведет к возникновению плохих вещей.

// 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 
+0

Подождите, я просто понял, что это был очень плохой пример. Поскольку я все еще держал ссылку, последняя содержит() фактически возвращает true. HashMaps - это другое дело, но это почти уходит на трехдневный уик-энд, и я не хочу выкапывать пример. –

+0

Хех, отправляй что-нибудь, когда вернешься - я с нетерпением жду этого. –

+0

Подождите, я забыл, что HashSet использует HashMap в качестве поддержки. Это действительно может произойти: поддержка HashMap использует hashCode для перехода к ведру, прежде чем проверять equals() на элементах в ведре, а изменение элемента приведет к тому, что он перейдет в неправильное ведро и не найдет там никаких элементов. –

0

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

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

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

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