代码混乱
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
,删除 k2
和 k2.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 不会找到该对象。
基本上,以更改哈希码的方式更改对象将完全搞砸该对象所属的任何 HashSet
或 HashMap
。 永远不要那样做。
您必须了解 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 后更改哈希码是一种不好的做法。
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
,删除 k2
和 k2.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 不会找到该对象。
基本上,以更改哈希码的方式更改对象将完全搞砸该对象所属的任何 HashSet
或 HashMap
。 永远不要那样做。
您必须了解 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 后更改哈希码是一种不好的做法。