Обходной путь для хеш-функции для HashSet при изменении внутреннего объекта - PullRequest
5 голосов
/ 10 января 2012

Ответ на этот SO объясняет возникшую у меня проблему: HashSet.remove () и Iterator.remove () не работают

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

Итак, так как этот ответ объясняет, что происходит, какой будет хороший обходной путь для того, чтобы иметь уникальность использования набора и иметь возможность изменять внутренние поля объектов в наборе? Или это просто невозможно?

Ответы [ 5 ]

7 голосов
/ 10 января 2012

Если изменяемые вами поля не являются частью теста на равенство, они также не должны быть частью вычисления хеш-кода. В этом случае нет проблем: вы можете просто изменить эти поля.

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

Если это последнее, и вы часто этим занимаетесь, возможно, вы захотите еще раз перейти к выбору структуры данных для рассматриваемой проблемы.

6 голосов
/ 10 января 2012

Удалите объект, который вы хотите изменить, из набора, измените его и добавьте обратно. Насколько я знаю, не существует стандартной реализации Set, которая могла бы справиться с изменением полей (которые используются в реализации hashCode() или compareTo()) во время хранения.

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

3 голосов
/ 10 января 2012

Единственный способ обойти это - не иметь метод hashCode(), который зависит от любых изменяемых полей. Если объекты имеют идентичность и существование, которые не зависят от значений его полей, то это легко - используйте System.identityHashCode(). В противном случае вы можете основать hashCode() на одном не изменяемом поле. Если его нет, боюсь, вам не повезло.

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

Используйте HashMap вместо HashSet. Определите ключ как нечто уникальное, которое не изменится во времени.

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

Используйте любую другую коллекцию (возможно, LinkedList) и проверяйте уникальность только в момент добавления, как в

public class MySetList<E> extends LinkedList<E> implements Set<E> {
    private static final long serialVersionUID = 1L;

    @Override
    public boolean add(E e) {
        return new HashSet<E>(this).add(e) ? super.add(e) : false;
    }
}
...