Как написать метод равенства в Java - PullRequest
5 голосов
/ 31 января 2012

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

public class Point {

    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    // ...
}

// мое определение равно

public boolean equals(Point other) {
  return (this.getX() == other.getX() && this.getY() == other.getY());
}

Что не так с этим методом? На первый взгляд все работает нормально:

Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);

Point q = new Point(2, 3);

System.out.println(p1.equals(p2)); // prints true

System.out.println(p1.equals(q)); // prints false

Однако неприятности начинаются, когда вы начинаете помещать очки в коллекцию:

import java.util.HashSet;

HashSet<Point> coll = new HashSet<Point>();
coll.add(p1);

System.out.println(coll.contains(p2)); // prints false

Как может случиться, что coll не содержит p2, даже если к нему добавлено p1, а p1 и p2 равные объекты?

Ответы [ 6 ]

8 голосов
/ 31 января 2012

Хотя это правда, что вы должны реализовать hashCode() при реализации equals(), это не вызывает вашей проблемы.

Это не тот метод equals(), который вы ищете. Метод equals всегда должен иметь следующую подпись: «public boolean equals (Object object)». Вот некоторый код.

public boolean equals(Object object)
{
  if (object == null)
  {
    return false;
  }

  if (this == object)
  {
    return true;
  }

  if (object instanceof Point)
  {
    Point point = (Point)object;
    ... now do the comparison.
  }
  else
  {
     return false;
  }
}

Класс Apache EqualsBuilder полезен для реализаций equals. Ссылка является более старой версией, но все еще применима.

Если вам понравился Apache EqualsBuilder, вам, вероятно, также понравится класс Apache HashCodeBuilder .

Редактировать: обновлен пример метода равенства для стандартных ярлыков.

4 голосов
/ 31 января 2012

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

Это объясняется более подробно, например, в Effective Java 2nd Edition , элемент 9: Всегда переопределять hashCode, когда вы переопределяете, равняется .

2 голосов
/ 31 января 2012

Хорошо работает, переопределяя хэш-код!

Всегда помните: переопределяйте hashCode, когда вы переопределяете равно.

@Override public int hashCode() {
        return (41 * (41 + getX()) + getY());
    }

Это мои реализации hashCode.

1 голос
/ 31 января 2012

В дополнение к другим ответам:

Если вы используете Eclipse в качестве IDE, вы можете просто использовать "Source" -> "Generate hashCode () и equals (), чтобы получить базовую реализацию. Делайте с этим все, что захотите.

1 голос
/ 31 января 2012

В соответствии с контрактом на equals() вам также необходимо реализовать hashCode().

Из JavaDoc на equals():

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

0 голосов
/ 31 января 2012

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

int hashCode() {
    return x * 31 + y;
}

Еще один момент (без каламбура): вы на самом деле не переопределяете метод equals(Object), определенный в классе Object, а вместо этого определяетеновенький.Правильный путь будет таким:

boolean equals(Object other) {
    if (other == this) return true;
    else if (!(other instanceof Point)) return false;
    else {
        Point p = (Point)other;
        return x == p.getX() && y == p.getY();
    }
}

Обратите внимание, что метод equals имеет довольно сильный контракт , связанный с ним, который вы должны выполнить.

...