@linux1s1s
2017-07-24T09:33:38.000000Z
字数 4204
阅读 1676
Java 2017-07
为了搞懂Java中
equals()和hashCode()方法,我们先从一个具体的问题入手。
直接看下面的实例Code
class Student {private int number;private String name;public Student(int number, String name) {this.number = number;this.name = name;}public int getNumber() {return number;}public void setNumber(int number) {this.number = number;}@Overridepublic String toString() {return "Student{" +"number=" + number +", name='" + name + '\'' +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}}
上面是典型的POJO类,接着我们实例化这个类,并且将实例化后的实例添加到Set容器中。
private void profile() {HashSet<Student> hs = new HashSet<>();Student s1, s2, s3;hs.add(s1 = new Student(1, "zhangsan"));hs.add(s2 = new Student(2, "lisi"));hs.add(s3 = new Student(1, "zhangsan"));Iterator<Student> it = hs.iterator();while (it.hasNext()) {Student s = it.next();Log.i("Student", s.toString());}}
上面故意将实例化的Student有两处内容相同,接下来观察一下打印出来的结果
07-24 16:47:22.420 29850-29850/ I/Student: Student{number=2, name='lisi'}07-24 16:47:22.420 29850-29850/ I/Student: Student{number=1, name='zhangsan'}07-24 16:47:22.420 29850-29850/ I/Student: Student{number=1, name='zhangsan'}
可以看到Set容器中出现了我们认为重复的实例(内容重复),而这在实际工作中,需要排除这种内容完全重复的实例,想一想在哪些地方要注意处理(其实在C端开发中,比如Tab页面,Server给出数据,如果不小心给出完全相同的数据,那么C端就要避免出现几个完全相同的页面同时出现)。那么是什么原因导致出现上面的情况呢?
我们先来看Set的add源码
java.util.HashSet.java
public boolean add(E e) {return map.put(e, PRESENT)==null;}
java.util.HashMap.java
/*** Adds a new entry with the specified key, value and hash code to* the specified bucket. It is the responsibility of this* method to resize the table if appropriate.** Subclass overrides this to alter the behavior of put method.*/public V put(K key, V value) {if (table == EMPTY_TABLE) {inflateTable(threshold);}if (key == null)return putForNullKey(value);int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);int i = indexFor(hash, table.length);for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {Object k;//1.Hash code 相同//2. key地址相同或者key满足equals条件//仅当以上条件都满足的情况下,不会将key,value添加到Entry容器if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null;}
定位到L22,我们看到在判断语句中分别调用了hash和equals()方法,如果hash code不同则直接加入Entry容器中,或者加入容器的实例不满足equals条件也会直接加入Entry容器。所以根据L19开始的注释得知,Student内容相同的两个实例必然满足如下条件:
Hash code不相同Student地址不相同且Student不满足equals条件我们来验证一下刚才的推测:
private void profile() {HashSet<Student> hs = new HashSet<>();Student s1, s2, s3;hs.add(s1 = new Student(1, "zhangsan"));hs.add(s2 = new Student(2, "lisi"));hs.add(s3 = new Student(1, "zhangsan"));Log.i("Student", "s1 hashcode: " + s1.hashCode());Log.i("Student", "s2 hashcode: " + s2.hashCode());Log.i("Student", "s3 hashcode: " + s3.hashCode());Log.i("Student", "s1.equals(s2): " + s1.equals(s2));Log.i("Student", "s2.equals(s3): " + s2.equals(s3));Log.i("Student", "s3.equals(s1): " + s3.equals(s1));Iterator<Student> it = hs.iterator();while (it.hasNext()) {Student s = it.next();Log.i("Student", s.toString());}}
验证结果:
07-24 17:23:37.372 1454-1454/ I/Student: s1 hashcode: 3616533407-24 17:23:37.372 1454-1454/ I/Student: s2 hashcode: 23382357507-24 17:23:37.372 1454-1454/ I/Student: s3 hashcode: 21574048407-24 17:23:37.372 1454-1454/ I/Student: s1.equals(s2): false07-24 17:23:37.372 1454-1454/ I/Student: s2.equals(s3): false07-24 17:23:37.372 1454-1454/ I/Student: s3.equals(s1): false07-24 17:23:37.372 1454-1454/ I/Student: Student{number=2, name='lisi'}07-24 17:23:37.372 1454-1454/ I/Student: Student{number=1, name='zhangsan'}07-24 17:23:37.372 1454-1454/ I/Student: Student{number=1, name='zhangsan'}
经过上面的验证,猜测正确,那么如果想完成实际开发中的要求(相同内容的实例不能同时加入到Set容器中),必须重写Student这个类的equals()和hashCode()方法。
重写Student类中的equals()和hashCode()方法
@Overridepublic boolean equals(Object obj) {Student s = (Student) obj;return this.number == s.number && this.name.equals(s.name);}@Overridepublic int hashCode() {return this.number * this.name.hashCode();}
验证结果
07-24 17:29:26.653 7938-7938/ I/Student: s1 hashcode: -143260455607-24 17:29:26.653 7938-7938/ I/Student: s2 hashcode: 664400607-24 17:29:26.653 7938-7938/ I/Student: s3 hashcode: -143260455607-24 17:29:26.653 7938-7938/ I/Student: s1.equals(s2): false07-24 17:29:26.653 7938-7938/ I/Student: s2.equals(s3): false07-24 17:29:26.653 7938-7938/ I/Student: s3.equals(s1): true07-24 17:29:26.653 7938-7938/ I/Student: Student{number=2, name='lisi'}07-24 17:29:26.653 7938-7938/ I/Student: Student{number=1, name='zhangsan'}
看到这里的s1和s3 hashcode相等,而且s1和s3满足equals条件,这个时候Set容器就自动屏蔽了内容相同的实例。
TIPS: 参看文章:Java提高篇——equals()与hashCode()方法详解
