1.功能
1.1 hashCode()
hashCode() 是 Java 中的一个方法,它用于计算对象的哈希码(hash code)。每个 Java 对象都可以调用 hashCode() 方法来获取其哈希码。
哈希码是一个整数值,用于快速确定对象是否相等。在 Java 中,哈希码通常用于优化数据结构和算法,例如散列集合(如 HashSet 和 HashMap)。
以下是 hashCode() 方法的常见特性:
-
一致性:如果对象的属性没有发生变化,多次调用 hashCode() 方法应该返回相同的值。但注意,当对象的属性改变时,其哈希码可能会改变。
-
相等性:根据 equals() 方法来定义的相等对象应具有相等的哈希码。也就是说,如果两个对象使用 equals() 判断为相等,则它们的哈希码必须相等。
-
效率:计算哈希码的算法应尽可能高效,以避免性能问题。通常情况下,良好的哈希算法应将不同的对象映射到不同的哈希码,以减少哈希冲突的可能性。
需要注意的是,重写 hashCode() 方法时,还应同时重写 equals() 方法,以确保二者的行为保持一致。
String.java
public int hashCode() {
// The hash or hashIsZero fields are subject to a benign data race,
// making it crucial to ensure that any observable result of the
// calculation in this method stays correct under any possible read of
// these fields. Necessary restrictions to allow this to be correct
// without explicit memory fences or similar concurrency primitives is
// that we can ever only write to one of these two fields for a given
// String instance, and that the computation is idempotent and derived
// from immutable state
int h = hash;
if (h == 0 && !hashIsZero) {
h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
if (h == 0) {
hashIsZero = true;
} else {
hash = h;
}
}
return h;
}
StringLatin1.java
public static int hashCode(byte[] value) {
int h = 0;
for (byte v : value) {
h = 31 * h + (v & 0xff);
}
return h;
}
1.2 equals()
==
比较的是对象的引用,equals()
只可被对象调用,在Object类中「equals()方法是通过“==”号比较两个实例变量是否指向同一个对象」,但是在派生类中一般会被重写,比较对象的内容是否相同;
String.java
@IntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) {
return false;
}
}
return true;
}
return false;
}
2. 二者联系
2.1 不用于哈希表
当自定义的类对象不用于【哈希表(hash)】时,不需要重写hashCode()
,直接继承Object类的方法(在Object类中,「hashCode()方法」是一个「native本地方法」,返回的是“对象对应的十进制的内存地址”。),可以根据需要重写equals()
即可
2.2 用于【哈希表】
用于【哈希表】且自定义类对象用作key时,需要重写hashCode()方法和equals()方法。
一定要重写原因:
- 既然equals()方法能够完全确定元素是否重复,那为什么还要加入hashCode()方法比较hashCode值呢?---- 因为重写的equals()方法中一般比较全面和复杂,导致效率比较低,而利用hashCode()进行对比,只需要生成一个hash Code值进行比较就可以了,效率很高。
- 那么既然hashCode()效率这么高为什么还要用equal()呢?---- 因为hashCode()并不是完全可靠的,有时候2对象的hashCode值相同,并不一定是重复的,也有可能是产生了“哈希冲突”,这个时候就需要使用equal()方法来判断“它们到底是重复元素,还是产生了‘哈希冲突’”(equal()返回true,则表示是重复元素;equals()返回false,则表示是产生了‘哈希冲突’)
一句话总结:先比较对象的hash值,如果hash值相等再equals()否则直接跳过。
源码分析: put方法返回值:previous value, or null if none
2.2.1 put方法
HashMap.java
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
//调用key的hashCode方法获得hash值
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
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;
// 根据hash值获取该对象对应位置在tab中的值
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
// 如果该位置存在值,hash值相等、对象引用一致或者值相等
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;
}
2.2.2get方法
public V get(Object key) {
Node<K,V> e;
return (e = getNode(key)) == null ? null : e.value;
}
final Node<K,V> getNode(Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n, hash; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
//获取key的hash值并且`(n - 1) & (hash = hash(key))`取得该位置的值
(first = tab[(n - 1) & (hash = hash(key))]) != null) {
// 比较对应对象的hash值是否相等,相等则继续比较key应用是否一致或者内容是否一致,返回节点
if (first.hash == hash && // always check first node
((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;
}