Почему я должен переопределить равняние и методы хэш-кода в Java?

Недавно я прочитал этот Документ Работ Разработчика.

Документ - все об определении hashCode() и equals() эффективно и правильно, однако я не могу выяснить, почему мы должны переопределить эти два метода.

Как я могу принять решение для реализации этих методов эффективно?

355
задан Exploring 20 March 2018 в 17:16
поделиться

7 ответов

Посмотрите на библиотеку ffmpeg-php. Это единственный простой способ манипулировать видео различных форматов в PHP.

Есть также обертка называется PHP Video Toolkit , вы можете найти его здесь: http://sourceforge.net/projects/phpvideotoolkit/

-121--3565850-

Я не думаю, что вы можете сделать совсем то, что вы хотите с MPI. Я программист Fortran, так что потерпите, если мое понимание Си немного шатко. Вы хотите, кажется, передать структуру данных, состоящую из 1 int и 1 последовательности (которые вы передаете, передавая расположение первого символа в последовательности) из одного процесса в другой? Я думаю, что вам придется пройти последовательность фиксированной длины - что, следовательно, должно было бы быть так долго, как любая из последовательностей, которые вы действительно хотите пройти. Зона приема для сбора этих последовательностей должна быть достаточно большой, чтобы принимать все последовательности вместе с их длиной.

Вероятно, вы захотите объявить новый тип данных MPI для ваших структур; Затем их можно собрать и, поскольку собранные данные включают длину последовательности, восстановить полезные части последовательности в приемнике.

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

-121--4817525-

Джошуа Блох говорит на эффективной Java

Вы должны переопределить hashCode () в каждом классе, который переопределяет равно (). Невыполнение этого требования приведет к нарушению общего договора на Object.hashCode (), что не позволит вашему классу нормально функционировать в сочетании со всеми коллекциями на основе хэша, включая HashMap, HashSet и Hashtable.

Попробуем понять это на примере того, что произойдет, если мы переопределим equals () без переопределения hashCode () и попытаемся использовать Map .

Скажем, что у нас есть такой класс, и что два объекта MyClass равны, если их поле равно (с hashCode () и equals () , генерируемыми затмением)

public class MyClass {

    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    public String getEqualField() {
        return importantField;
    }

    public String getAnotherField() {
        return anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }

}

Override only equals

Если переопределено только equals , то при вызове myMap.put(во-первых, someValue) сначала хэшируется в каком-то сегменте и при вызове myMap.put(во-вторых, thomeOtherValue) он будет хэширован в каком-либо другом сегменте (поскольку они имеют другой hashCode ). Таким образом, хотя они равны, так как они не хэшируются в одном ведре, карта не может понять это, и оба они остаются на карте.


Хотя нет необходимости переопределять равно () , если мы переопределяем hashCode () ,давайте посмотрим, что произойдет в этом конкретном случае, когда мы знаем, что два объекта MyClass равны, если их поле равно, но мы не переопределяем равно () .

Override only hashCode

Представьте, что это

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

Если переопределить только hashCode , то при вызове myMap.put (first, someValue) сначала вычисляет hashCode и сохраняет его в заданном сегменте. Затем при вызове myMap.put (second, someOtherValue) следует заменить первое на второе в соответствии с Картографической документацией , поскольку они равны (в соответствии с бизнес-требованием).

Но проблема заключается в том, что значение равно не было переопределено, поэтому, когда карта хэширует second и выполняет итерацию в области поиска объекта k так, что second.equals (k) является правдой, она не находит ничего, так как second.equals (первый) будет false .

Надеюсь, было ясно

496
ответ дан 23 November 2019 в 00:21
поделиться

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


из Effective Java, by Joshua Bloch

Последовательно определяя equals() и hashCode(), вы можете улучшить удобство использования ваших классов в качестве ключей в коллекциях на основе хэшей. Как объясняется в документации API для hashCode: "Этот метод поддерживается в интересах хэш-таблиц, таких как те, которые предоставляются java.util.Hashtable".

Лучшим ответом на ваш вопрос о том, как эффективно реализовать эти методы, будет предложение прочитать главу 3 книги Effective Java.

48
ответ дан 23 November 2019 в 00:21
поделиться
newmeths = {
  'two': lambda self: 2,
}

class MC(type):
  def __init__(cls, name, bases, dict):
    for k, v in newmeths.iteritems():
      setattr(cls, k, v)
    super(MC, cls).__init__(name, bases, dict)

class C(object):
  __metaclass__ = MC
  pass

c=C()
print c.two()
-121--2953999-

Поскольку они не переопределяются, в Object будет использоваться реализация по умолчанию.

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

-121--530669-

Предположим, что у вас есть класс (A), который объединяет два других класса (B) (C), и необходимо сохранить экземпляры (A) внутри хэш-таблицы. Реализация по умолчанию позволяет различать только экземпляры, но не по (B) и (C). Таким образом, два экземпляра A могут быть равны, но по умолчанию нельзя сравнивать их правильным путь.

3
ответ дан 23 November 2019 в 00:21
поделиться

Проще говоря, метод equals в Object проверяет равенство ссылок, в то время как два экземпляра вашего класса могут быть семантически равны, если их свойства равны. Это, например, важно при помещении объектов в контейнер, использующий equals и hashcode, как HashMap и Set. Допустим, у нас есть класс типа:

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }
}

Мы создаем два экземпляра с одинаковым id:

Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");

Без переопределения equals мы получим:

  • a.equals(b) ложно, потому что это два разных экземпляра
  • a.equals(a) истинно, потому что это один и тот же экземпляр
  • b.equals(b) истинно, потому что это один и тот же экземпляр

Правильно? Ну, может быть, если это то, что вам нужно. Но, допустим, мы хотим, чтобы объекты с одинаковым id были одним и тем же объектом, независимо от того, что это два разных экземпляра. Мы переопределим equals (и hashcode):

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Foo) {
            return ((Foo)other).id.equals(this.id);   
        }
    }

    @Override
    public int hashCode() {
        return this.id.hashCode();
    }
}

Что касается реализации equals и hashcode, я могу порекомендовать использовать вспомогательные методы Guava

21
ответ дан 23 November 2019 в 00:21
поделиться

Это полезно при использовании Объектов значений . Ниже приводится отрывок из Портлендского репозитория шаблонов :

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

Таким образом, у меня может быть несколько копий объекта , представляющего дату 16 января 1998 года. Любые из этих копий будут равны друг другу. Для небольшого объекта , такого как этот, часто проще создать новые и перемещать их, чем полагаться на один объект для представления Дата.

Объект значения всегда должен переопределять .equals () в Java (или = в Smalltalk). (Не забудьте переопределить .hashCode () как .)

​​
4
ответ дан 23 November 2019 в 00:21
поделиться

Потому что, если вы не переопределите их, вы будете использовать реализацию по умолчанию в Object.

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

6
ответ дан 23 November 2019 в 00:21
поделиться

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

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

0
ответ дан 23 November 2019 в 00:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: