Java HashSet с использованием указанного метода - PullRequest
3 голосов
/ 02 января 2011

У меня есть базовый класс HistoryItem, например:

public class HistoryItem
  private Date startDate;
  private Date endDate;
  private Info info;
  private String details;

  @Override
  public int hashCode() {
    int hash = (startDate == null ? 0 : startDate.hashCode());
    hash = hash * 31 + (endDate == null ? 0 : endDate.hashCode());
    return hash;
  }
}

В настоящее время я использую HashSet для удаления дубликатов из ArrayList в полях startDate & endDate, который работает правильно.

Однако мне также необходимо удалить дубликаты в разных полях (информация и подробности).

У меня такой вопрос.
Есть ли способ указать другой метод, который HashSet будет использовать вместо hashCode()?Примерно так:

public int hashCode_2() {
  int hash = (info == null ? 0 : info.hashCode());
  hash = hash * 31 + (details == null ? 0 : details.hashCode());
  return hash;
}

Set<HistoryItem> removeDups = new HashSet<HistoryItem>();
removeDups.setHashMethod(hashCode_2);

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

Ответы [ 6 ]

2 голосов
/ 02 января 2011

Вы можете создать класс обёртки вокруг HistoryItem с другой реализацией GetHashCode, а затем создать HashSet оберток вокруг каждого элемента в исходном наборе.

1 голос
/ 07 января 2011

Я использовал GNU Trove для этого.

Требуется минимальное изменение кода.

Новый класс, реализующий TObjectHashingStrategy (содержащий методы HashCode и Equals).

public class HistoryItemDuplicateInfo
implements TObjectHashingStrategy<HistoryItem> {

  @Override
  public int computeHashCode(HistoryItem obj) {
     ...
  }

  @Override
  public boolean equals(HistoryItem arg0, HistoryItem arg1) {
    ...
  }
}

Затем используйте объект THashSet с указанной стратегией для удаления дубликатов.

THashSet<HistoryItem> hs = new THashSet<HistoryItem>(new HistoryItemDuplicateInfo());

Надеюсь, что это поможет кому-то в будущем.

1 голос
/ 02 января 2011

Пара вещей. Прежде всего, вы ДОЛЖНЫ переопределить equals (), если вы собираетесь переопределить hashCode (). Это важно. Во-вторых, если вы имеете дело с разными полями, то, вероятно, у вас должен быть свой HashSet для каждого поля. Таким образом, вы можете перебирать карту так:

HashSet<String> info;
HashSet<String> details;
for (HistoryItem h:map){
  if(info.contains(h.getInfo()){
    // this is a dup

  }
  if (details.contains(h.getDetails()){
    // this is a dup
  }
  info.add(h.getInfo());
  details.add(h.getDetails());
}
0 голосов
/ 07 января 2011

HashSet жестко задан для использования hashCode() и equals().Вы могли бы реализовать свой собственный HashSet -подобный класс, возможно, безжалостно дублируя собственный исходный код Java, но это довольно уродливо, противоречит любому приличному набору правил разработки программного обеспечения и, возможно, незаконно в отношении лицензии на исходный код Java (это зависитфактический JDK, например, JDK от Sun / Oracle против OpenJDK).

Однако вы можете делать что-то с TreeSet.TreeSet обычно использует compareTo() метод элементов, не hashCode() или equals().Более того, экземпляр TreeSet может быть создан с пользовательским экземпляром Comparator, который затем вызывается для сравнения, позволяя вам иметь собственные правила.Метод compareTo() (или метод Comparator.compare()) должен реализовывать порядок , который может быть немного сложнее, чем простой hashCode() -and- equals(), но обычно это тоже не сложно.Иногда говорят, что TreeSet медленнее, чем HashSet, но фактическая разница незначительна, и для того, чтобы реально заметить эту разницу, требуется очень специфическая ситуация.

Концептуально, это может бытьхеш-эквивалент Comparator для HashSet: интерфейс HasherAndEqualizer с методами int hashCode(Object obj) и boolean equals(Object obj1, Object obj2).Sun не сочла нужным включить такой интерфейс, я не знаю почему.Возможно, они не думали, что это будет полезно.Библиотека GNU Trove, которую вы цитируете в другом ответе, предоставляет такой интерфейс.

В качестве альтернативы вы всегда можете использовать оболочки.Вместо хранения HistoryItem экземпляров во вторичном наборе вы можете хранить HistoryItemWrapper экземпляров, каждый из которых ссылается на фактический HistoryItem и предоставляет методы hashCode() / equals(), необходимые для этого набора.

0 голосов
/ 02 января 2011

Я бы предложил вам;

  • используйте long для даты вместо объекта Date.
  • используйте только Set, если вы хотите избежать дубликатов,Почему вы используете список вообще?Если вам нужно сохранить порядок, используя SortedSet, например TreeSet, или Set, который сохраняет порядок, такой как LinkedHashSet.
  • Может ли ваш HistoryItem быть действительным иметь нулевые поля?Можете ли вы структурировать свои поля так, чтобы они никогда не были нулевыми?
  • Поля, из которых состоит hashCode / equals / compareTo, должны быть неизменяемыми.Могут ли эти поля быть окончательными?Если нет, то почему?
0 голосов
/ 02 января 2011

Вы можете удалить дубликаты, используя java.util.TreeSet с пользовательским Comparator, который учитывает ваши Info и Details.

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