我遇到了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
}
我期待第二或第三输出可以给我一个真实,第四绝对是真的。但事实似乎并非同意。
关于我改变密钥的意图,我用地图来保存一些小怪。 mob有Point字段,地图的键使用相同的引用。然后我可以通过它的协调来寻找暴徒,也可以轻松地改变协调。
答案 0 :(得分:3)
您可以在source code中查找并了解它,尽管不像我想象的那么容易。
问题在于,当你更改x
的值时,你使哈希树中的对象看起来处于错误的位置,我将说明它。
将密钥放在如下所示的树中:
.../-- Point(0,0)
root
---\.. nothing
如果你把Point(1,0)放在里面,它会是这样的:
.../.. nothing
root
...\.. Point(1,0)
因此,当您更改密钥时,树看起来像这样:
.../.. Point(1,0)
root
...\.. nothing
现在你查找Point(1,0)
并且地图遍历了这样一个键所属的下部分支,但没有任何东西。当你寻找Point(0,0)
时,它会遍历错误键的上部分支。
因此,永远不要更改对象.hashCode()
的一部分值。而是使用该对象的新实例进行替换或使用Object的克隆放置在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
}
您可能希望将remove,alter和put方法转换为对象内部自己的方法以使其可用。您可以将其称为setAndRelocate(int newX, int newY, Map container)
并将其恢复为常规代码中的一行。
答案 1 :(得分:2)
由于HashMap的实施方式,你所获得的行为是正常的,但是会让人感到困惑。
粗略地说,在这里如何使用密钥K设置值V:
data[H] = Entry(K, V)
之后,当您致电get(K)
或containsKey(K)
时:
data[H]
是否为空并且等于您提供的密钥get
的值V或containsKey
在您的情况下,您使用Point(0,0)调用put
。假设Point(0, 0).hashcode()
返回0.因此该值存储在索引0中:
data = [Point(0, 0), null, ...]
然后修改点(a.x=1
)。密钥已修改,但HashMap无法检测到它,因此它不会将条目移动到正确的索引。
data = [Point(1, 0), null, ...]
之后,map.containsKey(new Point(0, 0))
返回false,因为根据containsKey
的工作原理如上所示:
Point(0, 0)
(您正在测试的密钥)不等于Point(1, 0)
(您存储的密钥),因此错误第三个测试也会打印错误,因为hashcode(Point(1, 0))
(您正在测试的密钥)可能并且确实从hashcode(Point(0, 0))
(您存储的密钥)返回不同的值。假设它返回1,而初始点存储在索引0处。这就是为什么它也返回false。
故事的故事: HashMap中使用的密钥必须是不可变的,否则会出现奇怪的行为。