Как исправить HashSet, который позволяет дубликаты? - PullRequest
0 голосов
/ 26 апреля 2019

Я использую java.util.Set интерфейс с java.util.HashSet реализацией и сохраняю его на карте.

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

При добавлении, казалось бы, равных объектов, Set.add возвращает true, и два равных объекта сохраняются в HashSet. Как это возможно и что я могу сделать, чтобы исправить это явное нарушение контракта Set?

Я использую Java 12 через IntelliJ IDEA 2018.3.6 и проверил мою java.lang.Object.hashCode реализацию класса двух объектов, которые я добавляю в Set, причем оба возвращают тот же хеш-код. Я также проверил реализацию java.lang.Objects.equals , и она возвращает true, когда метод используется для проверки их равенства. Оба объекта обернуты в другой объект, Entity, но он только перенаправляет реализации hashCode и equals объектов.

class Model {

    ...

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Offer)) {
            return false;
        }

        Offer offer = (Offer) obj;
        return Objects.equals(id, offer.id)
            && Objects.equals(name, offer.name)
            ;
    }

    @Override
    public int hashCode() {
        int result = 1;
        result = 31 * result + Objects.hashCode(id);
        result = 31 * result + Objects.hashCode(name);
        return result;
    }

    ...

}
class Store {

    ...

    private static class Entry {
        Object value;

        Entry(Object value) {
            this.value = value;
        }

        Object getValue() {
            return value;
        }

        @Override
        public boolean equals(Object obj) {
            return Objects.equals(value, obj);
        }

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

        @Override
        public String toString() {
            return "Entry[value = " + value + "]";
        }
    }

    ...

    private Map<Class<?>, Set<Entry>> data;

    ...

    private Set<Entry> get(Class<?> type) {
        return data.getOrDefault(type, new HashSet<>());
    }

    @Override
    public void persist(Object obj) {
        Entry entry = new Entry(obj);
        Set<Entry> objects = get(obj.getClass());
        if (objects == null) {
            objects = new HashSet<>();
        }
        if (!objects.add(entry)) {
            throw new ObjectExistsException
                ("Object already exists: " + obj);
        }
        data.put(obj.getClass(), objects);
    }

    ...

}

Когда obj1 и obj2 типа Model равны и objects уже содержит obj1, обернутый в объект Entry, я ожидаю, что obj2 не будет добавлен к objects, когда obj2 обернут в entry и для objects.add(entry), чтобы вернуть false, затем выдается исключение ObjectExistsException.

Однако в действительности objects.add(entry) возвращает true, а obj2, завернутый в entity, добавляется к objects.

Ответы [ 2 ]

2 голосов
/ 26 апреля 2019
    @Override
    public boolean equals(Object obj) {
        return Objects.equals(value, obj);
    }

Это неправильная реализация Entry.equals. Это потенциально сравнивает Entry со значением, хранящимся в текущей записи. (Как сравнение письма с конвертом).

Заставьте ваш метод equals проверить, что obj является Entry, и получите его значение, и проверьте равенство этому.

0 голосов
/ 26 апреля 2019

Ты был прав @ Энди Тернер, но именно @ Андреас указал мне правильное направление.Я думал, что буду ленивым и не напишу полную реализацию метода equals, но это стоило мне.Это должно было быть так:

private static class Entry {
    ...
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Entry)) {
            return false;
        }

        Entry entry = (Entry) obj;
        return Objects.equals(getValue(), entry.getValue());
    }
    ...
}

Спасибо вам обоим.

...