JDK 17的HashMap 提供了30个 public 方法。
Hash table 实现了 map接口。HashMap 和 Hash table大致相同,但是他是线程不安全的并且允许 null 值以及 null key。
Hash Map能够在常量的时间内实现get 和 put操作。
HashMap(int initialCapacity, float loadFactor)
输入初始容量,以及装载因子。其中初始容量最大为 2的30次方。值得注意的是,HashMap并不是按照指定的容量进行初始化,而是经过 tableSizeFor(initialCapacity) 操作后计算得出,大于 指定值且最接近指定值的 2的幂。也就意味着,HashMap的容量总是2的幂。
值得注意的是,HashMap的内存初始化容量确定是在第一次put操作,这点后面会进行讲解。
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
HashMap(int initialCapacity)
实际上调用的是HashMap(int initialCapacity, float loadFactor)。这里的装载因子默认为0.75f
HashMap()
只会设置默认的装载因子。
上面两个构造函引出了问题。什么时候确定容量呢?
HashMap(Map<? extends k>, ? extends V> m)
可以使用另一个Map来构造一个HashMap。默认装载因子为0.75.
V Put(K key, V value)
这里最常用的put方法其实是有返回值的,根据输入的key,如果key之前关联有value则返回value,如果没有则返回null。
实际上我们可以继续分析Put的具体实现,观察HashMap是如何来实现一个放入的具体函数实现。
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
让我们继续分析putval函数
hash(key)
我们观察到在放入的时候需要先对key进行hash。如果传入的key为null的话,那么返回0.如果传入的key不是null,那么返回一个根据key的hashcode处理后的hash。这样做的目的是为了让产生的hash更为分散。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
putVal(int hash, K key, V value, boolean onlyIfAbset, boolean evict)
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict)
这里传入的参数为 key处理后的hash以及 key value。 两个判断状态。onlyIfAbsent为true的时候表示不要改变现在存入的value,evict如果为false表示这个表在creation mode(没有明白)。
我们从put方法中可以看到,调用putVal时将OnlyIfAbsent传入false 表示可以更改其中存储的value,并且不在creation mode。
继续观察putVal的源码 (代续)。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
// 这里发现了新类 Node。
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;
}
3069

被折叠的 条评论
为什么被折叠?



