equals()和hashcode()
简单介绍
1 2 3 4 5 6 7 8 9 10 11 12 13
|
@HotSpotIntrinsicCandidate public native int hashCode();
public boolean equals(Object obj) { return (this == obj); }
|
hashcode()返回对象的哈希码值,符合一致性,同一对象无论多少次调用执行此方法都会返回相同整型结果,但hashcode()只用在散列表等类中。
equals()是基础类Object的成员函数,所以每个类都会继承此方法。其符合反射性,对称性,及物性,一致性。
最好的情况就是:如果两个对象相等,即equals(Object)返回真,分别调用hashcode()必须返回相同整型结果,若两个对象不相等,则hashcode()返回不同的整型结果。
但我们总说:equals相等则hashcode必定相等,hashcode相等equals未必相等
问题解答
问题1:默认的equals()与==有什么异同?
== 比较对象的引用地址是否相同,即是否为同一对象。equals()在没有被重写时等价于==,重写后根据覆盖的方法来判断是否相等。
那么equals()与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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| public class Student { private String id; private String name;
public Student(String id, String name) { this.id = id; this.name = name; }
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override public boolean equals(Object obj) { if(obj == null) return false; if(this == obj) return true; if(this.getClass() != obj.getClass()) return false; Student student = (Student) obj; return this.name.equals(student.getName()); } }
public class Test { public static void main(String[] args){ Student s1 = new Student("001","张三"); Student s2 = new Student("001","张三"); Student s3 = new Student("002","张三"); Student s4 = new Student("003","李四"); System.out.println("s1.equals(s2): " + s1.equals(s2)); System.out.println("s1.equals(s3): " + s1.equals(s3)); System.out.println("s1.equals(s4): " + s1.equals(s4)); System.out.println("s1.hashCode(): " + s1.hashCode()); System.out.println("s2.hashCode(): " + s2.hashCode()); System.out.println("s3.hashCode(): " + s3.hashCode()); System.out.println("s4.hashCode(): " + s4.hashCode()); } }
|
结果输出如下,普通对象使用时进行比较不会使用hashCode(),而equals()则根据具体的实现来确定返回结果。
1 2 3 4 5 6 7
| s1.equals(s2): true s1.equals(s3): true s1.equals(s4): false s1.hashCode(): 2088051243 s2.hashCode(): 1277181601 s3.hashCode(): 41903949 s4.hashCode(): 488970385
|
那么对于会创建类对应的散列表的对象呢?继续看以下的代码示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Test { public static void main(String[] args){ Student s1 = new Student("001","张三"); Student s2 = new Student("001","张三"); Student s3 = new Student("002","张三"); Student s4 = new Student("003","李四"); System.out.println("s1.equals(s2): " + s1.equals(s2)); System.out.println("s1.equals(s3): " + s1.equals(s3)); System.out.println("s1.equals(s4): " + s1.equals(s4)); System.out.println("s1: " + s1.toString() + "s1.hashCode(): " + s1.hashCode()); System.out.println("s2: " + s2.toString() + "s2.hashCode(): " + s2.hashCode()); System.out.println("s3: " + s3.toString() + "s3.hashCode(): " + s3.hashCode()); System.out.println("s4: " + s4.toString() + "s4.hashCode(): " + s4.hashCode());
HashSet set = new HashSet(); set.add(s1); set.add(s2); set.add(s3); set.add(s4); System.out.println("HashSet: " + set.toString()); } }
|
结果打印如下,可以看到Set中会有“重复”元素存在,因为我们只重写了equals(),而Set集合判断元素相等还需要哈希值相等,所以需要重写hashCode()
1 2 3 4 5 6 7 8
| s1.equals(s2): true s1.equals(s3): true s1.equals(s4): false s1: equals.Student@7c75222bs1.hashCode(): 2088051243 s2: equals.Student@7a0ac6e3s2.hashCode(): 2047526627 s3: equals.Student@71be98f5s3.hashCode(): 1908316405 s4: equals.Student@6fadae5ds4.hashCode(): 1873653341 HashSet: [equals.Student@6fadae5d, equals.Student@7a0ac6e3, equals.Student@71be98f5, equals.Student@7c75222b]
|
简单重写hashCode如下。
1 2 3 4
| @Override public int hashCode() { return this.name.toUpperCase().hashCode(); }
|
结果打印如下,具有相等哈希码值的对象不会再重复的存入Set了。
1 2 3 4 5 6 7 8
| s1.equals(s2): true s1.equals(s3): true s1.equals(s4): false s1: equals.Student@bd2e9s1.hashCode(): 774889 s2: equals.Student@bd2e9s2.hashCode(): 774889 s3: equals.Student@bd2e9s3.hashCode(): 774889 s4: equals.Student@cd94ds4.hashCode(): 842061 HashSet: [equals.Student@cd94d, equals.Student@bd2e9]
|
所以我们可以总结:对于哈希类,若两个对象相等,即equals()为真,则hashCode()一定相等,反之则不一定。因为hash公式不同的实现可能会导致不同的对象算出哈希值也相等(哈希碰撞),对于测试代码,因为实现的equals只比较name是否等值,而hashcode只比较name值对应的哈希码是否相等,所以两者应该是同步的。
对于HashSet为了优化速度,可以利用上述总结,判断元素是否重复时,先比较哈希值,当哈希值不同肯定不重复,哈希值相同再用equals()比较是否是相同元素,当equals()为false存入相同位置,所以每个哈希表中元素应该是由链式结构存储。
HashMap部分源代码如下,获取元素时是通过哈希码来查找的,再对链表的元素依次判断equals直到找到元素:
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; }
final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
public boolean add(E e) { return map.put(e, PRESENT)==null; }
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
|
参考博客:
https://www.cnblogs.com/skywang12345/p/3324958.html
因博客主未标明不可转载,若内容涉及侵权请及时告知,我会尽快修改和删除相关内容