使用 == 和 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 方法检查两个对象是否相同的。
如果相同的话就不会进行加入。
如果相同,就会重新散列到其他位置。