源码第一块:
概述:
Map 接口的基于哈希表的实现。此实现提供所有可选的映射操作,并允许 null 值和 null 键。(HashMap 类大致等同于 Hashtable,只不过它是不同步的,并且允许 null。此类不保证地图的顺序;特别是,它不保证订单会随着时间的推移保持不变。此实现为基本操作(get 和 put)提供恒定时间性能,假设哈希函数将元素正确地分散在存储桶中。对集合视图进行迭代所需的时间与 HashMap 实例的“容量”(存储桶数量)及其大小(键值映射数量)成正比。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或负载系数太低),这一点非常重要。HashMap 实例有两个影响其性能的参数:初始容量和负载因子。容量是哈希表中的存储桶数量,初始容量只是创建哈希表时的容量。负载因子是衡量哈希表在容量自动增加之前允许的满量。当哈希表中的条目数超过负载因子和当前容量的乘积时,将重新哈希表(即重新构建内部数据结构),以便哈希表的桶数大约是其两倍。作为一般规则,默认负载系数 (0.75) 在时间和空间成本之间提供了良好的权衡。较高的值会减少空间开销,但会增加查找成本(反映在 HashMap 类的大多数操作中,包括 get 和 put)。在设置映射的初始容量时,应考虑映射中的预期条目数及其负载系数,以最大程度地减少重新散列操作的次数。如果初始容量大于最大条目数除以负载因子,则不会发生重新散列操作。如果要在 HashMap 实例中存储许多映射,则创建具有足够大容量的映射将允许更有效地存储映射,而不是让它根据需要执行自动重新散列以增加表。请注意,使用具有相同 hashCode() 的多个键肯定会降低任何哈希表的性能。为了改善影响,当键具有可比性时,此类可以使用键之间的比较顺序来帮助打破联系。请注意,此实现不是同步的。如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改了映射,则必须在外部同步该映射。(结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已包含的键关联的值不是结构修改。这通常是通过在自然封装地图的某个对象上进行同步来实现的。如果不存在此类对象,则应使用 Collections.synchronizedMap 方法“包装”映射。这最好在创建时完成,以防止意外的不同步访问地图: Map m = Collections.synchronizedMap(new HashMap(...));此类的所有“集合视图方法”返回的迭代器都是快速失败的:如果在创建迭代器后的任何时间对映射进行结构修改,则迭代器将抛出 ConcurrentModificationException。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是冒着在未来不确定的时间出现任意的、非确定性行为的风险。请注意,无法保证迭代器的快速失效行为,因为一般来说,在存在不同步的并发修改的情况下,不可能做出任何硬性保证。快速失败迭代器会尽力而为地抛出 ConcurrentModificationException。因此,编写一个依赖于此异常的程序的正确性是错误的:迭代器的快速失败行为应该只用于检测错误。
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {}’
此映射通常充当 bined(桶)哈希表,但是当 bin 变得太大时,它们会转换为 TreeNodes 的 bin,每个 bin 的结构与 java.util.TreeMap 中的结构类似。大多数方法尝试使用普通的 bin,但在适用时中继到 TreeNode 方法(只需检查节点的实例)。TreeNodes 的 bin 可以像任何其他 bin 一样被遍历和使用,但在过度填充时还支持更快的查找。但是,由于正常使用的绝大多数箱不会过度填充,因此在表方法的过程中可能会延迟检查树箱是否存在。树 bin(即元素都是 TreeNodes 的 bin)主要由 hashCode 排序,但在平局的情况下,如果两个元素属于相同的“类 C 实现 Comparable
`/**
* The default initial capacity - MUST be a power of two.
* 默认容量大小为16,其必须为2的整数次幂
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16`
`/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*最大容量,如果使用参数的任一构造函数隐式指定了较高的值,则使用该容量。必须是 2 的幂<= 1<<30。
* 规定了HashMap的最大容量大小为2的29次方,如果手动输入了其他值,则采用改值,但是该值必须为2的幂``
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The load factor used when none specified in constructor.
* 负载因子,没什么好说的,网络上的文章已经写的很明白了
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
* 树化的阈值为8,当HashMap的容量大于64并且在一个bin上发生冲突,链的长度为8的时候,进行树化
* 该值必须大于 2,并且应至少为 8
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
* 解除树化的阈值为6,与8之间空着一个7,防止单个元素增减的时候频繁进行树化与解除
* 该值必须小于树化阈值,并且最多为6
*
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
* between resizing and treeification thresholds.
* 树化前置容量条件,当HashMap中的容量小于这个值的时候,达到了树化阈值,那么将会进行一次检定,如果容量
* 大于这个值,则进行树化,否则就进行扩容来减少Hash冲突
* 该值至少应该是4倍的树化阈值
*/
static final int MIN_TREEIFY_CAPACITY = 64;`
接下来是一个内部类Node,是HashMap进行元素存储的基本单位之一,这里就不过多赘述。
`static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
接下来是一些对外的方法:
/**
* 计算 key.hashCode() 并将 (XOR) 较高的哈希位传播到更低的位。由于该表使用 2 的幂掩码,因此仅以高于当前掩码的位数变化的哈希集将始终发生冲突。
* (在已知的例子中,有一组浮点键,它们在小表中保存连续的整数。因此,我们应用了一个转换
* ,将较高位的影响向下分散。在速度、实用性和比特扩展质量之间需要权衡。由于许多常
* 见的哈希集已经合理分布(因此不会从扩展中受益),并且由于我们使用树来处理 bin 中的大量冲突,因此我们只是以最便宜的方式对一些移位进行异或,
* 以减少系统损失,并合并最高位的影响,否则由于表边界,这些位永远不会用于索引计算。
* 这里计算hash是对key的hashCode进行高16位与低16位的异或,从而使得hash值同时具有高16位与低16位的特征,从而减少Hash冲突
*/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
* Returns x's Class if it is of the form "class C implements
* Comparable<C>", else null.
* 返回x的Class对象,当且仅当 X 是 X implement Comparable<X>,否则返回nul
*/
static Class<?> comparableClassFor(Object x) {
// 首先 x 必须是 Comparable的实现类
if (x instanceof Comparable) {
// 初始化 Class对象,type对象,参数化类型对象
Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
if ((c = x.getClass()) == String.class) // bypass checks
return c;
if ((ts = c.getGenericInterfaces()) != null) {
for (int i = 0; i < ts.length; ++i) {
if (((t = ts[i]) instanceof ParameterizedType) &&
((p = (ParameterizedType)t).getRawType() ==
Comparable.class) &&
(as = p.getActualTypeArguments()) != null &&
as.length == 1 && as[0] == c) // type arg is c
return c;
}
}
}
return null;
}
标签:bin,return,HashMap,映射,哈希,hashCode,源码,key,随笔
From: https://www.cnblogs.com/blog4cloudboy/p/17966460