【问题标题】:How Set checks for duplicates? Java HashSetSet 如何检查重复项? Java 哈希集
【发布时间】:2018-09-14 21:47:19
【问题描述】:

对于下面的代码,它输出“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());

    }
}

【问题讨论】:

  • 您只需将一个Object 添加到Set。您更改其内容的事实无关紧要
  • 那么我应该如何实现想要的输出呢?添加具有相同值的新对象也会创建两个新条目。
  • 您使用的是Set。两次添加对象将不起作用。第二个add 将返回false。因此,您没有再将t 添加到Set
  • 是的,如您的代码所示。试试这个set.add(t1);
  • 答案已经解释过了。

标签: java set hashset


【解决方案1】:

Set 实现可能调用t.hashCode(),并且由于数组不会覆盖Object.hashCode 方法,相同的对象将具有相同的哈希码。因此,更改数组的内容不会影响其哈希码。要正确获取数组的哈希码,您应该调用Arrays.hashCode

你不应该把可变的东西放在集合里,所以我建议你把不可变的列表放在集合里。如果您想坚持使用数组,只需创建一个新数组,就像您对 t1 所做的那样,然后将其放入集合中。

编辑:

对于代码 2,tt1 是两个不同的数组,因此它们的哈希码不同。同样,因为 hashCode 方法没有在数组中被覆盖。数组的内容不影响哈希码,无论它们是否相同。

【讨论】:

  • 我在第二个代码中这样做了,但设置大小变为 2。两个对象中的值相同。我也试过 int[] 。它具有相同的行为。
  • @Vivek t.hashCode() 返回一个与数组中的内容无关的值。它只是返回内存地址(这是默认的哈希码实现所做的)。
  • @Vivek Code 2 打印 2 因为集合中有两个 不同 数组对象。每当您使用new 时,您都在创建一个新对象。在代码 1 中,您将 t 添加到集合中,然后再次将相同的 t 添加到集合中。因此忽略了第二个添加。 t 第二次有不同的内容,但没有反映在它的哈希码中,所以集合忽略了第二次添加。
【解决方案2】:

Set 只包含不同的元素(这是它的本质)。基本实现 HashSet 使用 hashCode() 首先查找包含值的存储桶,然后使用 equals(Object) 查找不同的值。

数组很简单:它们的 hashCode() 使用默认值,继承自 Object,因此取决于引用。 equals(Object) 也与Object 相同:它只检查标识,即:引用必须相等。

定义为Java:

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

如果您想放置不同的数组,您必须尝试使用​​TreeSetComparator 的正确实现来试试运气,或者包装您的数组或使用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());

至于SetMap 键中使用的对象的可变性:

  • boolean equals(Object) 使用的字段不应被静音,因为被静音的对象可能等于另一个对象。 Set 将不再包含不同的值。
  • int hashCode() 使用的字段对于基于散列的集合(HashSetHashMap)不应静音,因为如上所述,它们通过将项目放入存储桶中进行操作。如果 hashCode() 发生变化,对象在存储桶中的位置很可能也会发生变化:Set 将包含两倍的相同引用。
  • int compareTo(T)Comparator::compare(T,T) 使用的字段不应因为与 equals 相同的原因而被静音:SortedSet 不会知道发生了变化。

如果需要,您必须先从集合中删除项目,然后对其进行变异,然后重新添加。

【讨论】:

    【解决方案3】:

    您将Object 添加到Set

    不包含重复元素。

    您只会在Set 中添加一个Object。您只需更改其内容的值。要了解我的意思,请尝试添加 System.out.println(set.add(t));

    作为add() 方法:

    如果此集合尚未包含指定元素,则返回 true

    此外,您的t1 与您的第一个代码 sn-p 完全无关,因为您从不使用它。


    在您的第二个代码 sn-p 中,它输出两个,因为您将两个 不同 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
    

    【讨论】:

    • 你能解释一下代码 2 发生了什么吗?
    【解决方案4】:

    java.util.Set 如何实现检查重复对象取决于实现,但根据Set 的文档,“重复”的适当含义是o1.equals(o2)

    由于HashSet 特别是基于哈希表,它会通过计算提供给它的对象的hashCode() 来寻找副本,然后遍历所有对象,如果有的话,在对应的哈希桶。

    数组不会覆盖hashCode()equals(),因此它们实现实例标识,而不是值标识。因此,无论其元素的值如何,给定数组始终具有相同的哈希码,并且始终具有 equals() 本身且仅其自身。您的第一个代码将相同的数组对象添加到集合中两次。无论其元素的值如何,它仍然是相同的集合。第二个代码将两个不同的数组对象添加到一个集合中。无论其元素的值如何,它们都是不同的对象。

    还要注意,如果您有实现值标识的可变对象,这样它们的相等性和哈希码取决于其成员的值,那么当它是 Set 的成员时修改这样的对象很可能打破Set。这是在每个实现的基础上记录的。

    【讨论】:

      猜你喜欢
      • 2019-04-13
      • 2011-12-28
      • 2013-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-29
      相关资源
      最近更新 更多