Как установить проверки на дубликаты? Java HashSet - PullRequest
0 голосов
/ 15 сентября 2018

Для приведенного ниже кода выводится «1». и второй код выводит "2", я не понимаю, почему это происходит. Это потому, что я добавляю тот же объект? Как мне добиться желаемого результата 2.

import java.util.*;
public class maptest {
public static void main(String[] args) {
    Set<Integer[]> set = new HashSet<Integer[]>();
    Integer[] t = new Integer[2];
    t[0] = t[1] = 1;
    set.add(t);
    Integer[] t1 = new Integer[2];
    t[0] = t[1] = 0;
    set.add(t);
    System.out.println(set.size());

   }
}

Второй код:

import java.util.*;
public class maptest {
public static void main(String[] args) {
    Set<Integer[]> set = new HashSet<Integer[]>();
    Integer[] t = new Integer[2];
    t[0] = t[1] = 1;
    set.add(t);
    Integer[] t1 = new Integer[2];
    t1[0] = t1[1] = 1;
    set.add(t1);
    System.out.println(set.size());

    }
}

Ответы [ 4 ]

0 голосов
/ 15 сентября 2018

Как java.util.Set реализации проверка для дублированных объектов зависит от реализации, но согласно документации Set, подходящее значение "дубликата" заключается в том, что o1.equals(o2).

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

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

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

0 голосов
/ 15 сентября 2018

Реализация Set, вероятно, вызывает t.hashCode(), и поскольку массивы не переопределяют метод Object.hashCode, один и тот же объект будет иметь тот же хеш-код. Таким образом, изменение содержимого массива не влияет на его хэш-код. Чтобы правильно получить хеш-код массива, вы должны вызвать Arrays.hashCode.

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

EDIT:

Для кода 2 t и t1 - это два разных массива, поэтому их хэш-код различен. Опять же, поскольку метод hashCode не переопределяется в массивах. Содержимое массива не влияет на хеш-код, независимо от того, являются ли они одинаковыми.

0 голосов
/ 15 сентября 2018

A Set содержит только отдельный элемент (это его природа). Базовая реализация, HashSet, использует hashCode(), чтобы сначала найти сегмент, содержащий значения, а затем equals(Object), чтобы найти другое значение.

Массивы просты: их hashCode () использует значение по умолчанию, унаследованное от Object и, следовательно, в зависимости от ссылки. equals(Object) также совпадает с Object: он проверяет только идентификатор, то есть: ссылки должны быть равны.

Определено как Java:

public boolean equals(Object other) {
  return other == this;
}

Если вы хотите поместить разные массивы, вам придется либо попытать счастья с TreeSet и правильной реализацией Comparator, либо обернуть ваш массив, либо использовать List или другой Set:

Set<List<Integer[]>> set = new HashSet<>();
Integer[] t = new Integer[]{1, 1};
set.add(Arrays.asList(t));
Integer[] t1 = new Integer[]{1, 1};
set.add(Arrays.asList(t1));
System.out.println(set.size());

Что касается изменчивости объекта, используемого в ключах Set или Map:

  • поля, используемые boolean equals(Object), не должны быть приглушены, потому что приглушенный объект может быть равен другому. Набор больше не будет содержать отдельных значений.
  • , используемые int hashCode(), не должны быть отключены для хэш-коллекции (HashSet, HashMap), потому что, как сказано выше, они работают, помещая элементы в корзину. Если hashCode () изменится, вполне вероятно, что место объекта в корзине также изменится: тогда Set будет содержать дважды одинаковую ссылку.
  • , используемые int compareTo(T) или Comparator::compare(T,T), не должны быть отключены по той же причине, что и equals: SortedSet не будет знать, что произошло изменение.

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

0 голосов
/ 15 сентября 2018

Вы добавляете Object к Set, который

не содержит повторяющихся элементов.

Вы добавляете только один Object к Set. Вы только изменяете значение его содержимого. Чтобы понять, что я имею в виду, попробуйте добавить System.out.println(set.add(t));.

Как метод add():

Возвращает true, если этот набор еще не содержит указанный элемент

Кроме того, ваш t1 совершенно не имеет значения в вашем первом фрагменте кода, поскольку вы никогда его не используете.


Во втором фрагменте кода он выводит два, потому что вы добавляете два разных Integer[] Objects к Set

Попробуйте распечатать хеш-код Objects, чтобы увидеть, как это работает:

Integer[] t = new Integer[2];
t[0] = t[1] = 1;
//Before we change the values
System.out.println(t.hashCode());
Integer[] t1 = new Integer[2];
t1[0] = t1[1] = 1;
//After we change the values of t
System.out.println(t.hashCode());
//Hashcode of the second object
System.out.println(t1.hashCode());

Выход:

//Hashcode for t is the same before and after modifying data
366712642
366712642
//Hashcode for t1 is different from t; different object
1829164700
...