前言
在编程的世界里,有一种神奇的工具,它小巧却强大,灵活而可靠,它是Java中最常用的数据结构之一,它就是HashMap。然而,你是否曾经好奇过,这么强大的工具,其背后的实现原理是怎样的呢?今天,让我们一起揭开HashMap源码的神秘面纱,探索这个Java世界中的瑞士军刀。
首先,我们需要明白,HashMap并不是一个简单的哈希表,它是一个基于哈希表实现的Map接口的子类。哈希表是一种数据结构,它通过哈希函数将键值对映射到数组的一个位置,从而实现快速查找。而HashMap则在此基础上,增加了一些额外的功能和优化,使得它在处理大量数据时更加高效。
源码分析
那么,HashMap是如何工作的呢?它的工作原理可以简化为以下几个步骤:
-
初始化:当我们创建一个HashMap对象时,它会调用构造函数,创建一个新的数组和一个空的Node对象列表。
/** * 系列构造方法,推荐在初始化时根据实际情况设置好初始容量,用好了可以显著减少 resize,提升效率 */ public HashMap(int initialCapacity, float loadFactor) { //检查初始容量是否小于0,如果是则抛出异常。 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); //检查初始容量是否大于最大容量,如果是则将初始容量设置为最大容量 if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; //检查负载因子是否小于等于0或者是非数字,如果是则抛出异常。 if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); //将传入的负载因子赋值给当前对象的负载因子。 this.loadFactor = loadFactor; //根据初始容量计算阈值并赋值给当前对象的阈值 this.threshold = tableSizeFor(initialCapacity); } //调用带默认负载因子的构造方法,传入初始容量。 public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } //调用无参构造方法,将默认负载因子赋值给当前对象的负载因子。 public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; } //调用带映射参数的构造方法,将传入的映射赋值给当前对象的键值对,并将默认负载因子赋值给当前对象的负载因子。 public HashMap(Map<? extends K, ? extends V> m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); }-
public HashMap(int initialCapacity, float loadFactor):这是一个带有初始容量和负载因子的构造方法。初始容量是HashMap在创建时可以容纳的元素数量,而负载因子是一个浮点数,表示HashMap在扩容之前可以达到的最大填充程度。如果初始容量小于0或者大于最大容量(MAXIMUM_CAPACITY),则会抛出异常。如果负载因子小于等于0或者是非数字(NaN),也会抛出异常。 -
public HashMap(int initialCapacity):这是一个只带有初始容量参数的构造方法,它调用了上一个构造方法,并使用默认的负载因子(DEFAULT_LOAD_FACTOR)。 -
public HashMap():这是一个无参构造方法,它设置了默认的负载因子(DEFAULT_LOAD_FACTOR)和其他字段的默认值。 -
public HashMap(Map<? extends K, ? extends V> m):这是一个带有Map参数的构造方法,它首先设置了默认的负载因子,然后调用了putMapEntries方法将传入的Map中的所有键值对放入HashMap中。
-
-
插入:当我们向HashMap中插入一个键值对时,首先会使用键的hashCode()方法计算出其在数组中的一个位置,然后检查该位置是否已经有Node对象存在。如果不存在,则创建一个新的Node对象并放入该位置;如果存在,则更新该Node对象的value字段。
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } 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; // 如果哈希表为空或者长度为0,则调用resize()方法进行扩容 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; // 如果当前节点的哈希值和键都匹配,则将当前节点赋值给e else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTre; // 如果当前节点是TreeNode类型,则调用putTre方法获取要插入的节点 else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); // 如果当前节点的下一个节点为空,则创建一个新的节点并放入当前节点的下一个位置 if (binCount >= TREEIFY_T

最低0.47元/天 解锁文章
7840

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



