Может ли hashCode () иметь динамически изменяемый контент?

#java

#java

Вопрос:

В моей реализации у меня есть класс A, который переопределяет equals (Object) и hashCode(). Но у меня есть небольшое сомнение в том, что при добавлении экземпляра A в HashSet / HashMap значение hashCode() равно x, через некоторое время значение того же hashCode() изменилось на y. Повлияет ли это на что-нибудь?

Ответ №1:

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

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

Ответ №2:

Когда возвращаемое значение hashCode() или equals() изменяется, пока объект содержится в HashMap / HashSet и т.д., Поведение не определено (вы можете получить все виды странного поведения). Поэтому следует избегать такой мутации ключей, пока объект содержится в таких коллекциях и т.д.

Считается лучшим использовать только неизменяемые объекты для ключей (или размещать их в HashSet и т.д.). На самом деле, например, python не разрешает использовать изменяемые объекты в качестве ключей в картах. В Java разрешено / распространено использовать изменяемые объекты в качестве ключей, но в таком случае желательно сделать такие объекты «эффективно неизменяемыми». Т.е. вообще не изменять состояние таких объектов после создания экземпляра.

Чтобы привести пример, использование списка в качестве ключа в Map обычно считается допустимым, но вам следует избегать изменения таких списков в любой точке вашего приложения, чтобы избежать появления неприятных ошибок.

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

Комментарии:

1. 1 за упоминание, что мы говорим о ключе, а не о значении. Хэш-код объекта value может измениться.

Ответ №3:

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

Редактировать: Как уже указывалось, это зависит от контейнера. Очевидно, что если контейнер никогда не использует ваши методы hashCode или equals , ничего не пойдет не так. Но как только он попытается сравнить вещи на предмет равенства (все карты и наборы), у вас возникнут проблемы.

Комментарии:

1. Есть ли какой-либо способ уведомлять об изменении содержимого в HashSet / HashMap?

2. @Jessu: Нет, не в Java HashSet / HashMap. Вам нужно было бы удалить объект, изменить его хэш-код, затем добавить его обратно.

3. Хэш-код может изменяться в течение срока службы объекта — просто, если он уже был добавлен на карту / набор, у вас возникнут проблемы. Можно (скажем) создать ArrayList, добавить в него элементы, а затем использовать это как ключ в HashMap при условии, что впоследствии он никогда не менялся .

4. @Mehrdad: Это указано в вопросе (совсем не подразумевается), но ваш ответ слишком широкий. Ваш ответ предполагает, что только неизменяемые типы вообще должны переопределять hashCode. Вам нужно быть гораздо более осторожным с изменяемыми типами, которые переопределяют hashCode, и понимать последствия этого, но это допустимо и не нарушает правил в Javadoc.

5. @Mehrdad @JonSkeet: фактическое правило таково: «Всякий раз, когда он вызывается для одного и того же объекта более одного раза во время выполнения Java-приложения, метод hashCode должен последовательно возвращать одно и то же целое число при условии, что никакая информация, используемая в сравнениях equals для объекта, не изменена. ‘ Javadoc для Object.hashCode().

Ответ №4:

ДА. Многие люди ответили на этот вопрос здесь, я просто хочу привести аналогию. Хэш-код — это что-то вроде адреса в коллекции на основе хэша:

Представьте, что вы регистрируетесь в отеле под своим именем «Майк», после этого вы меняете свое имя на «ГрейтМайк» на чековой бумаге. Затем, когда кто-то ищет вас по вашему имени «Майк», он больше не может вас найти.