HashSet становится ненадежным при изменении поля содержимого объекта. Почему / Когда или как я должен использовать HashSet? - PullRequest
0 голосов
/ 16 октября 2019

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

TestObject testObject = new TestObject(1, "hello");
Set<TestObject> set = new HashSet<>();
set.add(testObject);
testObject.number = 2;
set.add(testObject);
set.forEach(System.out::println);
//will print
//{number:2, string:hello}
//{number:2, string:hello}

Пример полного рабочего кода:

import java.util.*;

public class Main {

  public static void main(String[] args) {
    TestObject testObject = new TestObject(1, "hello");
    Set<TestObject> set = new HashSet<>();

     // add initial object
    set.add(testObject);

    // modify object
    testObject.number = 2;
    testObject.string = "Bye";

    // re-add same object
    set.add(testObject);
    set.forEach(System.out::println);
  }
}

class TestObject {

  public int number;
  public String string;

  public TestObject(int number, String string) {
    this.number = number;
    this.string = string;
  }

  @Override
  public int hashCode() {
    return Objects.hash(number, string);
  }

  @Override
  public boolean equals(Object obj) {
    if (!(obj instanceof TestObject)) {
      return false;
    }
    TestObject o = (TestObject) obj;
    return number == o.number && string.equals(o.string);
  }

  @Override
  public String toString() {
    return "{number:" + number + ", string:" + string + "}";
  }
}

Это означает, что после изменения объекта, который уже содержится в HashSet, theHashSet` становится ненадежным или недействительным.

Изменение объекта, который где-то содержится в Set (возможно, даже не зная), кажется мне обычным вариантом использования. И кое-что, что я, вероятно, уже сделал много.

Это отбрасывает меня назад и вызывает у меня один основной вопрос: когда или почему я должен использовать HashSet, если у него такое поведение?

Ответы [ 3 ]

1 голос
/ 16 октября 2019

Что ж, если вы посмотрите на источник HashSet, вы увидите, что это в основном HashMap<E, Object> с элементами, являющимися ключами - и изменение ключей hashmap никогда не было бы хорошей идеей. Карта / набор не будет обновляться, если хэш изменится, фактически карта / набор даже не будет знать об этом изменении.

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

Модификация объекта, который где-то содержится в наборе (возможно, даже не зная), кажется мне обычным вариантом использования. И кое-что, что я, вероятно, уже много сделал.

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

Таким образом, вы можете изменять элементы в HashSet, если вы не изменяете их"id".

Когда или почему я должен использовать HashSet, если он имеет такое поведение?

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

0 голосов
/ 16 октября 2019

Добавление элемента, который уже присутствует, как вы сказали, не переопределит элемент, который уже находится в HashSet. Используйте remove() перед вызовом add(), чтобы обеспечить эффективное добавление нового значения.

Примечание: как отметили некоторые пользователи, обратите внимание на сравнения строк в своем тесте.

0 голосов
/ 16 октября 2019

Вы никогда не должны сравнивать строки с == используйте вместо них .equals

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...