HashSet вставляет 2 равных элемента - PullRequest
0 голосов
/ 04 мая 2018

Я недавно работал над основной задачей, которая включала в себя сет, и я наткнулся на любопытную проблему. У меня есть следующий класс:

public static class Quadruple {
    int a;
    int b;
    int c;
    int d;
    Map<Integer, Integer> histogram;

    public Quadruple(int a, int b, int c, int d) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.histogram = new HashMap<>();
        histogram.put(a, histogram.get(a) == null ? 1 : histogram.get(a) + 1);
        histogram.put(b, histogram.get(b) == null ? 1 : histogram.get(b) + 1);
        histogram.put(c, histogram.get(c) == null ? 1 : histogram.get(c) + 1);
        histogram.put(d, histogram.get(d) == null ? 1 : histogram.get(d) + 1);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Quadruple)) {
            return false;
        }
        Quadruple q = (Quadruple) obj;
        return q.histogram.equals(this.histogram);
    }

    @Override
    public int hashCode() {
        return Objects.hash(a, b, c, d);
    }

Когда я инициализирую 2 объекта этого типа, вот так:

Quadruple q1 = new Quadruple(1, 1, 1, 2);
Quadruple q2 = new Quadruple(1, 1, 2, 1);

q1.equals (q2) возвращает true, но оба объекта могут быть добавлены в HashSet отдельно. Теперь из контракта HashSet я понимаю, что если объект, который вы пытаетесь добавить, равен () уже существующему объекту, его следует считать существующим, и ничего не следует делать. Мне удалось обойти эту проблему, используя LinkedList и проверяя, содержит ли список () объект перед его добавлением, что, похоже, работает соответствующим образом.

Мой вопрос в том, нормально ли это поведение, когда я проверил базовую реализацию и увидел, что HashMap, который используется в HashSet, на самом деле проверяет значения с помощью equals (). Есть ли что-то, что я могу упустить?

1 Ответ

0 голосов
/ 04 мая 2018

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

Если вы посмотрите на реализацию Objects.hash, вы получите этот код:

public static int hashCode(Object a[]) {
    if (a == null)
        return 0;

    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}

И как вы можете видеть в цикле, порядок аргументов, передаваемых Object.hash, имеет значение.

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

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