代码混乱

Confusion in a code

import java.util.*;
class KeyMaster {
  public int i;
  public KeyMaster(int i) { 
    this.i = i; 
  }

  public boolean equals(Object o) {
    return i == ((KeyMaster)o).i; 
  }
  public int hashCode() { 
    return i; 
  }
}
public class MapIt {
   public static void main(String[] args) {
   Set<KeyMaster> set = new HashSet<KeyMaster>();
   KeyMaster k1 = new KeyMaster(1);
   KeyMaster k2 = new KeyMaster(2);
   set.add(k1); set.add(k1);
   set.add(k2); set.add(k2);
   System.out.print(set.size() + “:”);
   k2.i = 1;
   System.out.print(set.size() + “:”);
   set.remove(k1);
   System.out.print(set.size() + “:”);
   set.remove(k2);
   System.out.print(set.size());
   }
}

关于输出的困惑 我正在尝试从集合中删除元素,输出仍然是

2
2
1
1

但预期输出是

2
2
1
0

k2.i = 1;set.remove(k2);之后执行时会尝试消除值为1和没有2的元素,所以最终的大小总是1。

the output is still 2 2 1 1

您正在重写 equals()hashcode() 方法。


首先 Sysout 打印大小 2,包含两个项目。

第二个 Sysout 打印尺寸 2,不从集合中删除任何项目。

第三个 Sysout 打印大小 1,删除 k1 已存在且值为 1.

第四个 Sysout 打印大小 1,删除 k2k2.i = 1 不存在。


编辑:

尝试使用 k2.i = 2; 删除 k2,因为它存在。然后你会看到预期的输出是 2 2 1 0.

一个 HashSet(或 HashMap)被组织为一个 "buckets" 的数组,其中每个桶包含一个值集合。当你对一个对象进行散列时,它会计算散列码 H,从 H 中确定桶号(通常是 H mod some-number),然后将对象放入该桶中。

假设哈希码为 1,对象被放入存储桶 1。现在假设您做了一些事情,例如更改 k2.i,从而更改了哈希码的值。 这不会导致对象被移动到不同的存储桶。(不能,因为无法在字段分配上设置 "trigger" 会导致HashSet 重新评估哈希码。)现在,当您尝试执行操作时,HashSet class 将计算新的哈希码,但对象现在位于错误的存储桶中.所以 class 不会找到该对象。

基本上,以更改哈希码的方式更改对象将完全搞砸该对象所属的任何 HashSetHashMap永远不要那样做。

您必须了解 HashSet 是如何工作的。 当你这样做时:

KeyMaster k = new KeyMaster(1);
set.add(k);

HashSet 获取 k 对象的哈希码,并将此哈希码与对象相关联。在这种情况下 k 将与 1.

相关联

现在您正在将 k 值更改

 k.i = 2;

k 对象的值已更改,但在 HashSet 中此对象仍与 1 相关联,因为 HashSet 已经记住以前的哈希码。

因此,当您尝试从 HashSet

中删除 k
set.remove(k);

HashSet 将得到 k 的哈希码。如您所知,哈希码已更改,它将变为 2,但在 HashSet 中,我们的对象仍与哈希码 1 相关联。这就是为什么 HashSet 无法找到并删除此对象。

实际上,在将对象添加到 HashSet 后更改哈希码是一种不好的做法。