一旦某个对象作为哈希表的键存储时,就不要去改变该对象内部的任何字段内容。因为一个对象的哈希值是根据它内部的字段值进行计算的,而一旦将该对象作为哈希表的键存储,它的存储位置就和自己的哈希值直接相关。那么任何改变对象内容的操作都会相应的改变其哈希值,哈希值一旦改变就极有可能无法找到该对象在哈希表中的存储位置,这个时候就基本上没有希望找到哈希表中以该对象对应的键值对了,这就发生了哈希错乱。所以,反过来说,如果一个对象在使用过程中可能会改变自身内容,那么就不要将其作为哈希表的键。请看一个栗子:
HashMap<Book, String> data = new HashMap<>();
Book book1 = new Book("Gold Apple", "Introduction of the misery " +
"journey of growing gold apples");
data.put(book1, "The first book of our life.");
Book book2 = new Book("Silver Apple", "Introduction of the misery " +
"journey of growing silver apple");
data.put(book2, "The second book of our life.");
System.out.println(data.get(book1)); // [1]
book1.setName("Copper Apple");
System.out.println(data.get(book1)); // [2]
Book book3 = new Book("Gold Apple", "Introduction of the misery " +
"journey of growing gold apples");
System.out.println(data.get(book3)); // [3]
上例中,[1] 处代码可以正常找的book1
键对应的值 "The first book of our life.";
而 [2] 处代码无法找到任何值,因为book1
的name
被改变造成其哈希值被改变,所以根据新的book1
无法找到正确的存储位置,就算找到的位置的键非null
(假设为 e
),也不能通过equals()
判断,即book1.equals(e) = false
因此只能得到null
;
[3] 处代码试图通过创建一个跟原来对象内容相同的对象来取出对应位置存储的值,但是也不行, 因为此时虽然哈希值正确,找到了正确的存储位置,但是不能通过equals()
判断,即book3.equals(book1) = false
,因此依然只能得到null
。
只有从一开始就不改变book1
的任何内容,即注释掉第 10 行,那么后面两次的查询才都会得到正确的值。
因此,改变键对象的内容会造成很严重的后果。结论是,哈希表中的键对象内容不能作任何改变。