使用 == 和 equals
== :判斷兩個對象的地址是否相等。比較其內存地址
equlas:作用是比較兩個對象是否相等,存在兩種情況
情況1:類沒有覆蓋重寫equals方法,則使用的是父類 Object 的 equals 方法。即通過 “==” 比較兩個對象內存地址。
情況2:如果覆蓋重寫了equals方法,一般,比較兩個對象的內容是否相等。
比如在String 類中的equals方法被重寫過:

public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
關於自定義類中比較對象對象是否相等的實現:
--- 首先先了解什么是hashcode,作用 ---
hashcode就是散列碼,使用高效率的哈希算法來定位查找對象。
比如 String str = "abcd",那么計算機會先計算器散列碼,再將其放入到對應的內存數組索引處。
在Object類中的 toString 方法:
return getClass().getName() + “@” + Integer.toHexString(hashCode());
在String類中的hashCode算法
public int hashCode() { int h = hash; //private int hash; // Default to 0 if (h == 0 && value.length > 0) { char val[] = value; //value是存儲的字符數組 for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
數學表達式:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
s[ i ] 即String 的第 i 個字符,n 是 str 的長度。
上述表達式的計算過程:
String str = "abcd"; h = 0 value.length = 4 val[0] = a val[1] = b val[2] = c val[3] = d h = 31*0 + a = a h = 31 * (31*0 + a) + b = 31 * a + b h = 31 * (31 * (31*0 + a) + b) + c = 31 * (31 * a + b) + c = 31 * 31 * a + 31 * b + c h = 31 * (31 * 31 * a + 31 * b + c) + d = 31 * 31 * 31 * a + 31 * 31 * b + 31 * c + d h = 31 ^ (n-1) * val[0] + 31 ^ (n-2) * val[1] + 31 ^ (n-3) * val[2] + ...+ val[n-1]
-->雖然 字符串長度太長,int類型可能丟失數據,但是依然可以起到散列的作用。
為什么使用 31 作為散列碼,而不是其他的數字?
計算機的乘法會涉及到移位計算,選擇 31 的原因是一個素數。
質數又稱素數。指在一個大於1的自然數中,除了1和此整數自身外,沒法被其他自然數整除的數
在存儲數據,計算hash地址的時候,我們希望能盡量較少同樣的 hash 地址,即“hash沖突”。
如果得到的相同的 hash 地址過多的話,那么這些數據組成的hash鏈(鏈表)就會更長,從而會降低查詢的效率。
所以在選擇系數的時候選擇盡量長的(31=11111 [2] )系數且乘法盡量不要溢出(大於31 的話,很容易溢出)的系數。
如果計算出來的Hash 地址越大,那么沖突就會越小,查找的效率也會提高。
31 可以由 i*31 == (i<<5)-i 表示,使用31 是為了更好地分配hash (int類型) 地址。並且31 只占用了 5 bits。
Integer 類的最大值是 : 2147483647
在java乘法中如果數字相乘過大,會導致移除的問題,導致數據的丟失。
--- Objects 類中的 hash() 方法實現 ---
public static int hash(Object... values) { return Arrays.hashCode(values); } | | Arrays類的hashCode() 方法 V public static int hashCode(Object a[]) { if (a == null) return 0; int result = 1; for (Object element : a) result = 31 * result + (element == null ? 0 : element.hashCode()); return result; } | | Object的hashCode() 方法 V public native int hashCode();
-->Object 類中的 hashCode 方法,是本地方法,也就是C語言實現。該方法將對象的內存地址轉換成 int 類型整數返回。
--- 關於hashCode 和equals 方法的重寫 ---
public class Person { private String name; private int age; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); } }

public boolean add(E e) { return map.put(e, PRESENT)==null; } | | V public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } | | V static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } | | V 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) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
--- 為什么要有hashCode ---
在將對象加入到HashSet 的時候,在HashMap 中會先計算對象的 hashcode 值來判斷對象加入的位置。
如果沒有相等的 hashcode 則表示該對象沒有添加過。進行添加。
如果存在星等的 hashcode(由於哈希碰撞),則使用 equals 方法檢查兩個對象是否相同的。
如果相同的話就不會進行加入。
如果相同,就會重新散列到其他位置。