Вам нужно переопределить hashCode () и equals () для записей?

Предположим следующий пример:

public record SomeRecord(int foo, byte bar, long baz)
{ }

Мне нужно переопределить hashCode а также equals если бы я добавил указанный объект в HashMap?

3 ответа

Решение

Нет, вам не нужно определять свое собственное hashCode а также equals. Вы можете сделать это, если хотите изменить реализацию по умолчанию.

См. Подробности в разделе 8.10.3 спецификации https://docs.oracle.com/javase/specs/jls/se14/preview/specs/records-jls.html

Обратите внимание, в частности, на предупреждение о реализации вашей собственной версии:

Все члены унаследованы от java.lang.Record. Если явно не переопределено в теле записи, R имеет неявно объявленные методы, которые переопределяют методы equals, hashCode и toString из java.lang.Record.

Если какой-либо из этих методов из java.lang.Record явно объявлен в теле записи, реализации должны удовлетворять ожидаемой семантике, указанной в java.lang.Record.

В частности, обычай equalsреализация должна удовлетворять ожидаемой семантике, согласно которой копия записи должна быть равна записи. Обычно это не так для классов (например, дваCar объекты могут быть equals если их VIN значение такое же, даже если ownerполя разные), но для записей должно быть верно. Это ограничение означало бы, что редко есть причина отменятьequals.

Ответ на вопрос, нужно ли вам это или нет, действительно будет - это зависит от реализации сущности, которую вы решили создать какRecord. Нет никаких ограничений при компиляции или во время выполнения, которые ограничивали бы вас, и это всегда имело место для классов, расширяющихObject тем не мение.

головы

С другой стороны, одним из основных мотивов для предложения был "малоценный, повторяющийся, подверженный ошибкам код: конструкторы, средства доступа,equals(), hashCode(), toString()и т. д.". В носителях данных это довольно часто подразумевается в современном программировании на Java. Следовательно, решение, как указано далее, состояло в том, чтобы предпочесть семантические цели и

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

хвосты

Итак, о шаблоне позаботились, но обратите внимание, вы все еще можете по какой-то причине захотеть, чтобы один из ваших компонентов записи не рассматривался как часть процесса сравнения между двумя разными объектами, и именно здесь вы можете захотеть переопределить реализация по умолчаниюequals а также hashCodeпри условии. Кроме того, я не сомневаюсь в фантазиях, которые иногда требуются отtoStringи, следовательно, необходимость переопределить его.

Вышеупомянутое в большинстве случаев не может быть отнесено к категории сбоев компиляции или выполнения, но в самом предложении говорится о риске, с которым оно связано:

Любой из членов, которые автоматически выводятся из описания состояния, также можно объявить явно. Однако небрежная реализация методов доступа или equals/hashCode рискует подорвать семантические инварианты записей.

(Примечание: последнее в основном мое мнение, так что потребители будут стремиться к всевозможным гибким возможностям, чтобы они могли использовать новейшие функции, но так, как существующая реализация, используемая для работы. Видите ли, обратная совместимость имеет большее значение, поскольку ну во время апгрейдов.)

Что такое запись Java? Одна из самых распространенных жалоб на Java заключается в том, что вам нужно написать много кода, чтобы класс был полезным. Довольно часто нужно написать следующее:

  1. нанизывать()
  2. хэш-код()
  3. равно ()
  4. Методы получения
  5. Публичный конструктор

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

Цель записей - расширить синтаксис языка Java и создать способ сказать, что класс - это "поля, только поля и ничего, кроме полей". Сделав это заявление о классе, компилятор может помочь, автоматически создав все методы и включив все поля в такие методы, как hashCode().

Записи поставляются с реализацией по умолчанию дляhashCode(), equals() а также toString() для всех атрибутов внутри записи

Реализация hashCode() по умолчанию

Запись будет использовать хэш-код всех атрибутов внутри записи.

Реализация по умолчанию equals()

Запись будет использовать все атрибуты, чтобы решить, равны ли записи буксировки или нет.

Таким образом, любые реализации хеширования, например HashSet, LinkedHashSet, HashMap, LinkedHashMap,

и т. д. будут использовать hashCode() и в случае столкновения будет использовать equals()

Реализация по умолчанию или индивидуальная?

Если вы хотите использовать все атрибуты в hashCode() а также equals() тогда не отменяйте

Вам нужно переопределить hashCode() и equals() для записей?

Вы можете сохранить реализацию по умолчанию или выбрать для нее только некоторые атрибуты.

что угодно, но если вам нужен настраиваемый атрибут, вы можете переопределить, какие атрибуты определяют равенство, а атрибуты делают hashCode

Как рассчитывается хэш-код в реализации по умолчанию?

будет использовать хэш-код целого числа и строки, например:

    int hashCode = 1 * 31;
    hashCode = (hashCode + "a".hashCode()) & 0x7fffffff;

, Код ниже с реализацией по умолчанию дляhashCode(), equals()

а также toString().

HashSet не добавил их два, потому что две записи имеют одинаковый хэш-код и равны

    static record Record(int id, String name) {

    }

    public static void main(String[] args) {
        Record r1 = new Record(1, "a");
        Record r2 = new Record(1, "a");

        Set<Record> set = new HashSet<>();
        set.add(r1);
        set.add(r2);
        System.out.println(set);

        System.out.println("Hashcode for record1: " + r1.hashCode());
        System.out.println("Hashcode for record2: " + r2.hashCode());

        int hashCode = 1 * 31;
        hashCode = (hashCode + "a".hashCode()) & 0x7fffffff;
        System.out.println("The hashCode: " + hashCode);
    }

, выход

[Record[id=1, name=a]]
Hashcode for record1: 128
Hashcode for record2: 128
The hashCode: 128

, Ресурсы:

блоги оракул

Другие вопросы по тегам