Java hashmap如何保存和查找密钥

Java hashmap如何保存和查找密钥,java,hashmap,Java,Hashmap,我遇到了一个关于HashMap的问题。经过一些没有结果的调试后,我编写了一小段代码来测试HashMap。但结果却更加令人困惑 public static void main(String[] args) { Point a = new Point(0, 0); // java.awt.Point Map<Point, Integer> map = new HashMap<Point, Integer>(); map.put(a,

我遇到了一个关于HashMap的问题。经过一些没有结果的调试后,我编写了一小段代码来测试
HashMap
。但结果却更加令人困惑

public static void main(String[] args) {
    Point a = new Point(0, 0);          // java.awt.Point
    Map<Point, Integer> map = new HashMap<Point, Integer>();
    map.put(a, 0);
    System.out.println(map.containsKey(new Point(0, 0)));    // true
    a.x = 1;
    System.out.println(map.containsKey(new Point(0, 0)));    // false
    System.out.println(map.containsKey(new Point(1, 0)));    // false
    System.out.println(map.containsKey(a));                  // false
}
publicstaticvoidmain(字符串[]args){
点a=新点(0,0);//java.awt.Point
Map Map=newhashmap();
map.put(a,0);
System.out.println(map.containsKey(新点(0,0));//true
a、 x=1;
System.out.println(map.containsKey(新点(0,0));//false
System.out.println(map.containsKey(新点(1,0));//false
System.out.println(map.containsKey(a));//false
}
我希望第二个或第三个输出可以给我一个真的,第四个肯定是真的。但事实似乎并非如此


关于我想换钥匙的事,我用地图救了一些暴徒。mob有点域,地图的键使用相同的引用。然后,我可以通过协调来寻找暴徒,也可以轻松地更改协调。

您可以在中查找并理解它,尽管不像我想的那么容易

问题是,当您更改
x
的值时,会使哈希树中的对象似乎位于错误的位置,我将对此进行说明

将密钥放置在一棵树中,该树如下所示:

.../-- Point(0,0)
root
---\.. nothing
.../.. nothing
root
...\.. Point(1,0)
.../.. Point(1,0)
root
...\.. nothing
如果你把点(1,0)放在里面,它会是这样的:

.../-- Point(0,0)
root
---\.. nothing
.../.. nothing
root
...\.. Point(1,0)
.../.. Point(1,0)
root
...\.. nothing
因此,当您更改密钥时,树如下所示:

.../-- Point(0,0)
root
---\.. nothing
.../.. nothing
root
...\.. Point(1,0)
.../.. Point(1,0)
root
...\.. nothing
现在,您查找
点(1,0)
,映射将遍历该键所属的较低分支,但没有任何内容。当你寻找
点(0,0)
时,它会穿过上面的分支,那里有错误的键

因此,绝对不要更改对象的
.hashCode()
部分的值,这一点很重要。而是使用该对象的新实例进行替换,或者使用对象的克隆放置在
HashMap

或者,如果您想更改关键点,可以将其从地图中删除,更改它并将其放回。像这样:

public static void main(String[] args) {
    Point a = new Point(0, 0);          // java.awt.Point
    Map<Point, Integer> map = new HashMap<Point, Integer>();
    map.put(a, 0);
    System.out.println(map.containsKey(new Point(0, 0)));    // true
    Integer value = map.remove(a);
    a.x = 1;
    map.put(a,value);
    System.out.println(map.containsKey(new Point(0, 0)));    // false
    System.out.println(map.containsKey(new Point(1, 0)));    // true
    System.out.println(map.containsKey(a));                  // true
}
publicstaticvoidmain(字符串[]args){
点a=新点(0,0);//java.awt.Point
Map Map=newhashmap();
map.put(a,0);
System.out.println(map.containsKey(新点(0,0));//true
整数值=映射。删除(a);
a、 x=1;
map.put(a,值);
System.out.println(map.containsKey(新点(0,0));//false
System.out.println(map.containsKey(新点(1,0));//true
System.out.println(map.containsKey(a));//true
}

您可能希望将remove、alter和put方法转移到对象中自己的方法中,以使其可用。您可以调用它
setAndRelocate(int-newX,int-newY,Map-container)
,并将其返回到常规代码中的一行。

由于HashMap的实现方式,您得到的行为是正常的,但会令人困惑

大致上,这里是用键K放置值V的工作原理:

  • HashMap计算密钥K的hashcode H
  • 在存储值的数组中,H用作索引(必要时进行修剪):
    data[H]=条目(K,V)
之后,当您调用
get(K)
containsKey(K)
时:

  • HashMap计算密钥的哈希代码H
  • 然后检查
    data[H]
    是否为空,以及它的key是否等于您提供的key
  • 如果是,则返回值V表示
    get
    ,或返回值true表示
    containsKey
在您的例子中,您使用点(0,0)调用了
put
。假设
点(0,0)。hashcode()
返回0。因此,该值存储在索引0中:

data = [Point(0, 0), null, ...]
然后修改该点(
a.x=1
)。密钥已修改,但HashMap无法检测到它,因此它不会将条目移动到正确的索引

data = [Point(1, 0), null, ...]
之后,
map.containsKey(新点(0,0))
返回false,因为根据
containsKey
的工作方式,如上所示:

  • hashcode返回0(与最初放置时相同)
  • 但是,
    点(0,0)
    (您正在测试的密钥)不等于
    点(1,0)
    (您存储的密钥),因此为false
第三个测试也会打印false,因为
hashcode(点(1,0))
(您正在测试的键)可能会返回一个与
hashcode(点(0,0))
(您存储的键)不同的值。假设它返回1,而初始点存储在索引0处。这就是为什么它也返回false


故事的士气:HashMap中使用的键必须是不变的,否则会出现奇怪的行为

什么是
?它是否有适当的
hashCode()
equals()
实现?此外,切勿修改在
映射中用作键的元素
必须覆盖
hashCode()
equals()
HashMap
Point.hashCode()
的值作为键value@drkunibar第一个true是否表示方法已被重写?可能重复