java map equals_理解HashMap中equals和hashCode的工作原理

HashMap使用hashCode(),==并equals()用于条目查找。给定键的查找序列k如下:

使用k.hashCode()来确定条目存储其斗,如果有的话

如果找到,对于k1该桶中的每个条目的密钥,如果k == k1 || k.equals(k1),则返回k1的条目

任何其他结果,没有相应的条目

为了演示一个例子,假设我们想要创建一个HashMapwhere键,如果它们具有相同的整数值(由AmbiguousIntegerclass 表示),则它们在逻辑上是等价的。然后我们构造一个HashMap,放入一个条目,然后尝试覆盖其值并按键检索值。

class AmbiguousInteger {

private final int value;

AmbiguousInteger(int value) {

this.value = value;

}

}

HashMap map = new HashMap<>();

// logically equivalent keys

AmbiguousInteger key1 = new AmbiguousInteger(1),

key2 = new AmbiguousInteger(1),

key3 = new AmbiguousInteger(1);

map.put(key1, 1); // put in value for entry '1'

map.put(key2, 2); // attempt to override value for entry '1'

System.out.println(map.get(key1));

System.out.println(map.get(key2));

System.out.println(map.get(key3));

Expected: 2, 2, 2

不要覆盖hashCode()和equals():在默认情况下的Java生成不同的hashCode()不同对象的值,因此HashMap使用这些值映射key1和key2成不同的桶。key3没有相应的桶,所以它没有价值。

class AmbiguousInteger {

private final int value;

AmbiguousInteger(int value) {

this.value = value;

}

}

map.put(key1, 1); // map to bucket 1, set as entry 1[1]

map.put(key2, 2); // map to bucket 2, set as entry 2[1]

map.get(key1); // map to bucket 1, get as entry 1[1]

map.get(key2); // map to bucket 2, get as entry 2[1]

map.get(key3); // map to no bucket

Expected: 2, 2, 2

Output:   1, 2, null

hashCode()仅覆盖: HashMap映射key1并key2进入同一个存储桶,但由于两者key1 == key2和key1.equals(key2)检查失败,它们保持不同的条目,因为默认情况下equals()使用==check,它们引用不同的实例。key3失败都==和equals()检查对key1和key2,因此具有没有对应的值。

class AmbiguousInteger {

private final int value;

AmbiguousInteger(int value) {

this.value = value;

}

@Override

public int hashCode() {

return value;

}

}

map.put(key1, 1); // map to bucket 1, set as entry 1[1]

map.put(key2, 2); // map to bucket 1, set as entry 1[2]

map.get(key1); // map to bucket 1, get as entry 1[1]

map.get(key2); // map to bucket 1, get as entry 1[2]

map.get(key3); // map to bucket 1, no corresponding entry

Expected: 2, 2, 2

Output:   1, 2, null

equals()仅覆盖: HashMap由于默认值不同,将所有密钥映射到不同的存储桶hashCode()。==或者equals()检查在这里是无关紧要的,因为它HashMap永远不会达到需要使用它们的程度。

class AmbiguousInteger {

private final int value;

AmbiguousInteger(int value) {

this.value = value;

}

@Override

public boolean equals(Object obj) {

return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;

}

}

map.put(key1, 1); // map to bucket 1, set as entry 1[1]

map.put(key2, 2); // map to bucket 2, set as entry 2[1]

map.get(key1); // map to bucket 1, get as entry 1[1]

map.get(key2); // map to bucket 2, get as entry 2[1]

map.get(key3); // map to no bucket

Expected: 2, 2, 2

Actual:   1, 2, null

覆盖both hashCode()和equals():HashMapmaps key1,key2并覆盖key3到同一个存储桶中。==在比较不同的实例时检查失败,但是equals()检查通过,因为它们都具有相同的值,并且被我们的逻辑视为“逻辑上等效”。

class AmbiguousInteger {

private final int value;

AmbiguousInteger(int value) {

this.value = value;

}

@Override

public int hashCode() {

return value;

}

@Override

public boolean equals(Object obj) {

return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;

}

}

map.put(key1, 1); // map to bucket 1, set as entry 1[1]

map.put(key2, 2); // map to bucket 1, set as entry 1[1], override value

map.get(key1); // map to bucket 1, get as entry 1[1]

map.get(key2); // map to bucket 1, get as entry 1[1]

map.get(key3); // map to bucket 1, get as entry 1[1]

Expected: 2, 2, 2

Actual:   2, 2, 2

如果hashCode()随机怎么办?:HashMap将为每个操作分配一个不同的存储桶,因此您永远不会找到您之前输入的相同条目。

class AmbiguousInteger {

private static int staticInt;

private final int value;

AmbiguousInteger(int value) {

this.value = value;

}

@Override

public int hashCode() {

return ++staticInt; // every subsequent call gets different value

}

@Override

public boolean equals(Object obj) {

return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;

}

}

map.put(key1, 1); // map to bucket 1, set as entry 1[1]

map.put(key2, 2); // map to bucket 2, set as entry 2[1]

map.get(key1); // map to no bucket, no corresponding value

map.get(key2); // map to no bucket, no corresponding value

map.get(key3); // map to no bucket, no corresponding value

Expected: 2, 2, 2

Actual:   null, null, null

如果hashCode()总是一样的话怎么办?:HashMap将所有键映射到一个大桶中。在这种情况下,您的代码在功能上是正确的,但使用HashMap实际上是多余的,因为任何检索都需要在O(N)时间内迭代该单个存储桶中的所有条目(或Java 8的O(logN)),等效使用a List。

class AmbiguousInteger {

private final int value;

AmbiguousInteger(int value) {

this.value = value;

}

@Override

public int hashCode() {

return 0;

}

@Override

public boolean equals(Object obj) {

return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;

}

}

map.put(key1, 1); // map to bucket 1, set as entry 1[1]

map.put(key2, 2); // map to bucket 1, set as entry 1[1]

map.get(key1); // map to bucket 1, get as entry 1[1]

map.get(key2); // map to bucket 1, get as entry 1[1]

map.get(key3); // map to bucket 1, get as entry 1[1]

Expected: 2, 2, 2

Actual:   2, 2, 2

如果equals总是假的怎么办?:==当我们将同一个实例与自身进行比较时检查通过,但是否则失败,equals检查总是失败key1,key2并且key3被认为是“逻辑上不同”,并且映射到不同的条目,尽管它们仍然在同一桶中hashCode()。

class AmbiguousInteger {

private final int value;

AmbiguousInteger(int value) {

this.value = value;

}

@Override

public int hashCode() {

return 0;

}

@Override

public boolean equals(Object obj) {

return false;

}

}

map.put(key1, 1); // map to bucket 1, set as entry 1[1]

map.put(key2, 2); // map to bucket 1, set as entry 1[2]

map.get(key1); // map to bucket 1, get as entry 1[1]

map.get(key2); // map to bucket 1, get as entry 1[2]

map.get(key3); // map to bucket 1, no corresponding entry

Expected: 2, 2, 2

Actual:   1, 2, null

好的,如果equals现在总是如此?:你基本上是说所有对象都被认为与另一个对象“在逻辑上等同”,所以它们都映射到同一个桶(由于相同hashCode()),相同的条目。

class AmbiguousInteger {

private final int value;

AmbiguousInteger(int value) {

this.value = value;

}

@Override

public int hashCode() {

return 0;

}

@Override

public boolean equals(Object obj) {

return true;

}

}

map.put(key1, 1); // map to bucket 1, set as entry 1[1]

map.put(key2, 2); // map to bucket 1, set as entry 1[1], override value

map.put(new AmbiguousInteger(100), 100); // map to bucket 1, set as entry1[1], override value

map.get(key1); // map to bucket 1, get as entry 1[1]

map.get(key2); // map to bucket 1, get as entry 1[1]

map.get(key3); // map to bucket 1, get as entry 1[1]

Expected: 2, 2, 2

Actual:   100, 100, 100

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值