hashCode 与 equals
二者都是Object默认的方法,默认情况下,equals()比较两个对象的内存地址是否相等。hashCode()用于Hash表快速查找对象,hashCode()也由对象内存地址决定(值不是内存地址)。以下程序查看对象的内存地址以及hashCode。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// HashTest.java public class HashTest { } // Test.java public class Test { public static void main(String[] args) { HashTest a = new HashTest(); HashTest b = new HashTest(); System.out.println(a+", "+b); System.out.println(a.hashCode()+", "+b.hashCode()); } } // 输出 HashTest@1540e19d, HashTest@677327b6 356573597, 1735600054 |
equals 用于判断对象是否相同,那 hashCode 的作用是什么?
hashCode 主要用于 Hash 表,我们知道 Hash 表查询速度很快,其采用“数组+链表”的存储方式,通过特定的哈希函数,将所要存储的数据映射到对应的地址,下次访问直接计算得到内存地址取值,当多个存储对象出现哈希冲突时,采用拉链法,从该位置建立链表,当采用的哈希函数适当时,数据能够均匀分布,查询速度接近O(1)。
不难看出,hashCode 用于第一步寻址,直接找到目标或链表头结点,而 equals 顺着链表找到目标。
重写 hashCode 与 equals
为什么要重写?因为很多时候我们会遇到这种场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// Person.java public class Person { String name; public Person(String name) { this.name = name; } } // Main.java public class Main { public static void main(String[] args) { HashMap<Person, Integer> ps = new HashMap<Person, Integer>(); ps.put(new Person("wang"), 100); System.out.println(ps.get(new Person("wang"))); } } // 输出 null |
分析一下,为什么输出是 null
, 因为通过new
创建的新对象与 HashMap 中的对象 hashCode() 不一致,equals() 更不一致,在第一步查找定位就失败了。
因此,我们必须重写上述两个方法, 重写的代码很繁琐,所幸 Ideaj 和 Eclipse 都可以自动快速帮助我们生成。Idea 下通过 Alt + Insert 可以调出自动生成菜单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// Person.java public class Person { String name; public Person(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return !(name != null ? !name.equals(person.name) : person.name != null); } @Override public int hashCode() { return name != null ? name.hashCode() : 0; } } |
这里的 hashCode() 默认使用了字符串的哈希值计算方法,哈希函数影响查询的效率,我们也能自定义哈希函数,例如经典的取模法,以下是一个取模法的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// Employee.java public class Employee { int id; public Employee(int id) { this.id = id; } @Override public int hashCode() { return id % 10; } } // Test.java public class Test { public static void main(String[] args) { Employee a = new Employee(1); Employee b = new Employee(1); Set<HashTest> set = new HashSet<HashTest>(); set.add(a); set.add(b); System.out.println(a.hashCode()+", "+b.hashCode()); System.out.println(a.equals(b)); System.out.println(set); } } // 输出 1, 1 false [HashTest@1, HashTest@1] |
在HashSet.add(b)时,先查找是否有对应的hashCode,再使用equals()比较对象,如果有同一对象存在,则不执行add操作。由于没有重写 equals() 方法,HashSet 添加了两个对象,其 hashCode 均为 1。
总结
hashCode() 和 equals() 是哈希表存储和查询对象的重要依据,两者必须同时重写。
不同对象 equals() => false,但 hashCode 可以相同。
本文参考:
Pretty nice post. I just stumbled upon your blog and wanted
to say that I've truly enjoyed browsing your blog
posts. In any case I'll be subscribing to your rss feed and I hope you write again soon!
Howdy would you mind sharing which blog platform you're working
with? I'm planning to start my own blog soon but I'm having a hard time selecting between BlogEngine/Wordpress/B2evolution and Drupal.
The reason I ask is because your layout seems different then most blogs and I'm looking for something completely unique.
P.S My apologies for getting off-topic but I had to ask!
I'm using WordPress with the theme 'Kratos'. You can find it on https://github.com/Vtrois/Kratos.