hashmap如何保存和查找密钥

时间:2014-04-15 05:49:06

标签: 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, 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字段,地图的键使用相同的引用。然后我可以通过它的协调来寻找暴徒,也可以轻松地改变协调。

2 个答案:

答案 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:

  • HashMap计算密钥K的哈希码H
  • H用作存储值的数组中的索引(必要时修剪):data[H] = Entry(K, V)

之后,当您致电get(K)containsKey(K)时:

  • HashMap计算密钥的哈希码H
  • 然后检查data[H]是否为空并且等于您提供的密钥
  • 如果是,则返回get的值V或containsKey
  • 的true

在您的情况下,您使用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的工作原理如上所示:

  • hashcode返回0(与最初设置时相同)
  • 但是,Point(0, 0)(您正在测试的密钥)不等于Point(1, 0)(您存储的密钥),因此错误

第三个测试也会打印错误,因为hashcode(Point(1, 0))(您正在测试的密钥)可能并且确实从hashcode(Point(0, 0))(您存储的密钥)返回不同的值。假设它返回1,而初始点存储在索引0处。这就是为什么它也返回false。

故事的故事: HashMap中使用的密钥必须是不可变的,否则会出现奇怪的行为