/** * 默认初始容量,默认为2的4次方 = 16,2的n次方是为了加快hash计算速度,;;减少hash冲突,,,h & (length-1),,1111111 */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 /** * 最大容量,默认为2的30次方, */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * 负载因子(load factor),它用来衡量哈希表的 空/满 程度,一定程度上也可以体现查询的效率, * 计算公式为:负载因子 = 总键值对数 / 箱子个数 * 负载因子越大,意味着哈希表越满,越容易导致冲突,性能也就越低。 * 因此,一般来说,当负载因子大于某个常数(可能是 1,或者 0.75 等)时,哈希表将自动扩容 * * 默认负载因子,默认为0.75 */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * 如果哈希函数不合理,即使扩容也无法减少箱子中链表的长度, * 因此 Java 的处理方案是当链表太长时,转换成红黑树。 * 这个值表示当某个箱子中,链表长度大于 8 时,有可能会转化成树 */ static final int TREEIFY_THRESHOLD = 8; /** * 在哈希表扩容时,如果发现链表长度小于 6,则会由树重新退化为链表 */ static final int UNTREEIFY_THRESHOLD = 6; /** *在转变成树之前,还会有一次判断,只有键值对数量大于 64 才会发生转换。 *这是为了避免在哈希表建立初期,多个键值对恰好被放入了同一个链表中而导致不必要的转化 */ static final int MIN_TREEIFY_CAPACITY = 64; /** * HashMap内部类实现了Map的内部类Entry,用于存储K,V */ 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; } /** * 重写equals方法用于判断两个K-V映射是否相等 * @param o * @return */ 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; } } /* ---------------- Static utilities -------------- */ /** * Computes key.hashCode() and spreads (XORs) higher bits of hash * to lower. Because the table uses power-of-two masking, sets of * hashes that vary only in bits above the current mask will * always collide. (Among known examples are sets of Float keys * holding consecutive whole numbers in small tables.) So we * apply a transform that spreads the impact of higher bits * downward. There is a tradeoff between speed, utility, and * quality of bit-spreading. Because many common sets of hashes * are already reasonably distributed (so don't benefit from * spreading), and because we use trees to handle large sets of * collisions in bins, we just XOR some shifted bits in the * cheapest possible way to reduce systematic lossage, as well as * to incorporate impact of the highest bits that would otherwise * never be used in index calculations because of table bounds. */ 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. */ static Class<?> comparableClassFor(Object x) { if (x instanceof Comparable) { 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; } /** * Returns k.compareTo(x) if x matches kc (k's screened comparable * class), else 0. */ @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable static int compareComparables(Class<?> kc, Object k, Object x) { return (x == null || x.getClass() != kc ? 0 : ((Comparable)k).compareTo(x)); } /** * 返回一个2的倍数的数 最接近cap的. */ static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; } /* ---------------- Fields -------------- */ /** * The table, initialized on first use, and resized as * necessary. When allocated, length is always a power of two. * (We also tolerate length zero in some operations to allow * bootstrapping mechanics that are currently not needed.) */ transient Node<K,V>[] table; /** * Holds cached entrySet(). Note that AbstractMap fields are used * for keySet() and values(). */ transient Set<Map.Entry<K,V>> entrySet; /** * K-v键值对映射元素的个数 */ transient int size; /** *Hash表结构性修改次数,用于实现迭代器快速失败行为 */ transient int modCount; /** * 当前Hash表的下一次扩容的容量阀值. */ int threshold; /** * 当前Hash表的负载因子 * * @serial */ final float loadFactor; /* ---------------- Public operations -------------- */ /** *制定初始容量和负载因子的构造方法 */ 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); } /** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and the default load factor (0.75). * * @param initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } /** * Constructs a new <tt>HashMap</tt> with the same mappings as the * specified <tt>Map</tt>. The <tt>HashMap</tt> is created with * default load factor (0.75) and an initial capacity sufficient to * hold the mappings in the specified <tt>Map</tt>. * * @param m the map whose mappings are to be placed in this map * @throws NullPointerException if the specified map is null */ public HashMap(Map<? extends K, ? extends V> m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); } /** * Implements Map.putAll and Map constructor * * @param m the map * @param evict false when initially constructing this map, else * true (relayed to method afterNodeInsertion). */ final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) { int s = m.size(); if (s > 0) { if (table == null) { // pre-size float ft = ((float)s / loadFactor) + 1.0F; int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY); if (t > threshold) threshold = tableSizeFor(t); } else if (s > threshold) resize(); for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) { K key = e.getKey(); V value = e.getValue(); putVal(hash(key), key, value, false, evict); } } } /** * Returns the number of key-value mappings in this map. * * @return the number of key-value mappings in this map */ public int size() { return size; } /** * Returns <tt>true</tt> if this map contains no key-value mappings. * * @return <tt>true</tt> if this map contains no key-value mappings */ public boolean isEmpty() { return size == 0; } /** * Returns the value to which the specified key is mapped, * or {@code null} if this map contains no mapping for the key. * * <p>More formally, if this map contains a mapping from a key * {@code k} to a value {@code v} such that {@code (key==null ? k==null : * key.equals(k))}, then this method returns {@code v}; otherwise * it returns {@code null}. (There can be at most one such mapping.) * * <p>A return value of {@code null} does not <i>necessarily</i> * indicate that the map contains no mapping for the key; it's also * possible that the map explicitly maps the key to {@code null}. * The {@link #containsKey containsKey} operation may be used to * distinguish these two cases. * * @see #put(Object, Object) */ public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } /** * Implements Map.get and related methods * * @param hash hash for key * @param key the key * @return the node, or null if none */ final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; /** * 1:将tab指向table,并校验tab不为空(即table不为空) * 2:n赋值为table的长度,校验n>0(说明有K-V键值对元素) * 3:将tab的(n - 1) & hash元素赋值给first,校验first不为空 * 以上条件全部符合,说明key可能存在,需要遍历first连接的整个链表(也可能是红黑树) * */ if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & hash]) != null) { /** * 从首节点开始匹配 * 1)校验哈希值是否一样 * 2)校验key是否相等,此步骤分以下两种情况 * 1:key的引用相等 * 2:key不为null且key的内容一样(如果一个自定义类进行比较,必须重写hashCode和equals方法) */ 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; } /** * Returns <tt>true</tt> if this map contains a mapping for the * specified key. * * @param key The key whose presence in this map is to be tested * @return <tt>true</tt> if this map contains a mapping for the specified * key. */ public boolean containsKey(Object key) { return getNode(hash(key), key) != null; } /** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } /** * Implements Map.put and related methods * * @param hash hash for key * @param key the key * @param value the value to put * @param onlyIfAbsent if true, don't change existing value * @param evict if false, the table is in creation mode. * @return previous value, or null if none */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; /** * 1:将tab指向table,并校验tab不为空(即table不为空) * 2:n赋值为table的长度,校验n>0(说明有K-V键值对元素) * 符合以上任一条件,说明当前数组没被初始化,需要初始化 */ if ((tab = table) == null || (n = tab.length) == 0) /** * 初始化数组并赋值给tab * 将tab的长度赋值给n */ n = (tab = resize()).length; /** * tab的(n - 1) & hash位置元素不空,就将数据插入到此位置(插入一个包含K-V键值对的Node节点) */ if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { /** * 说明哈希冲突了,需要通过链表或红黑树(链表长度大于某个阀值会转为红黑树)解决 * 找到hash和key都相同的那个节点 */ 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; } } //已存在K的映射 if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value;//覆盖value afterNodeAccess(e); return oldValue; } } ++modCount; //size大于扩容阀值时进行扩容 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; } /** * Initializes or doubles table size. If null, allocates in * accord with initial capacity target held in field threshold. * Otherwise, because we are using power-of-two expansion, the * elements from each bin must either stay at same index, or move * with a power of two offset in the new table. * * @return the table */ final Node<K,V>[] resize() { //指向旧的数组 Node<K,V>[] oldTab = table; //旧数组的容量 int oldCap = (oldTab == null) ? 0 : oldTab.length; //旧的扩容阀值 int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { //如果旧的数组容量大于MAXIMUM_CAPACITY,就将扩容阀值设为Integer.MAX_VALUE,返回旧的数组,不进行扩容操作 if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } /** * 1:将newCap设为旧数组容量的两倍 * 2:newCap小于MAXIMUM_CAPACITY 且 oldCap不小于DEFAULT_INITIAL_CAPACITY */ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) //扩容阀值设为旧阀值两倍 newThr = oldThr << 1; // double threshold } //只是初始化了扩容阀值,没有创建数组 else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults /** * 说明数组、阀值都没有初始化 * 容量设为默认16 * 阀值=16*0.75 */ newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } /** * 出现newThr = 0的条件 * 1)oldCap > 0 且 (oldCap*2 >= MAXIMUM_CAPACITY或oldCap <DEFAULT_INITIAL_CAPACITY) * 2) oldThr有值 */ if (newThr == 0) { //新容量*负载因子得出一个阀值 float ft = (float)newCap * loadFactor; /** * 新容量小于MAXIMUM_CAPACITY且新得出阀值<MAXIMUM_CAPACITY 新阀值就是ft * 否则新阀值为Integer.MAX_VALUE */ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } //设置扩容阀值为新阀值 threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) //创建一个新的数组,大小为新容量 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; //旧数组不为空时,需要将数据拷贝到新数组内 if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K,V> e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // preserve order Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; /** * (e.hash & oldCap)=0表示就算数组扩容之后,通过hash计算出来的索引还是一样 * 不需要调整位置 */ if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } /** * 需要调整索引 * 新索引=原索引+oldCap */ else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
相关推荐
Java集合之HashMap用法详解 Java集合之HashMap用法详解主要介绍了Java集合之HashMap用法,结合实例形式分析了java map集合中HashMap定义、遍历等相关操作技巧。 HashMap概述 HashMap是Java集合框架中最常用的Map...
ArrayList集合与HashMap的扩容原来 ArrayList扩容原理 ArrayList集合的底层是数组,特点是查询快增删慢。当创建一个集合时,ArrayList集合的空参构造底层数组默认长度为 0,即new Object[0]。当添加第一个元素时,...
这篇7页的PDF文档“java集合类HashMap总结”可能是对HashMap类的深入解析,包括其原理、常用方法以及在实际开发中的应用。 HashMap的核心特性在于它的哈希函数,这个函数将键(key)转换为一个哈希码(hash code)...
在Java编程中,HashMap集合是开发者经常使用的数据结构之一,尤其在处理大量数据时,它的高效性和灵活性使得它成为首选。HashMap是Java集合框架的一部分,位于`java.util`包下,实现了Map接口,用于存储键值对(key-...
HashMap是Java集合框架的一部分,它实现了Map接口,用于存储键值对。其核心特点在于基于哈希表的映射机制,能够通过键快速检索对应的值。HashMap支持键和值为null,同时是非线程安全的,即其方法不是同步的。 2. ...
在Java编程语言中,集合框架是开发者日常工作中不可或缺的一部分,HashMap作为其中的重要成员,它的实现原理对于理解Java性能优化和数据结构有深远的意义。HashMap是一个基于哈希表的数据结构,它实现了Map接口,...
HashMap是一种常用的哈希表实现,用于存储键值对。它实现了Map接口,并且使用键的哈希值来快速定位和访问值。
Java集合,HashMap底层实现和原理(1.7数组+链表与1.8+的数组+链表+红黑树) 在Java集合中,HashMap是一个常用的数据结构,它基于Map接口实现,元素以键值对的方式存储,并且允许使用null键和null值。由于key不允许...
HASHMap迭代集合的例子好用,逻辑算法
Delphi的HashMap集合是一种高效的数据结构,用于存储键值对,它通过哈希函数实现快速查找,这使得在大量数据中查找特定元素的时间复杂度接近O(1)。HashMap在Delphi的不同版本中都有支持,包括Delphi 5、Delphi 6、...
Java集合专题总结:HashMap和HashTable源码学习和面试总结 本文总结了Java集合专题中的HashMap和HashTable,涵盖了它们的源码学习和面试总结。HashMap是一种基于哈希表的集合类,它的存储结构是一个数组,每个元素...
集合,ArrayList,hashmap
HashMap作为Java集合框架中的重要成员,是面试中常见的知识点,尤其在数据结构与算法、并发编程以及JVM内存管理等领域,HashMap的深入理解至关重要。本篇将围绕HashMap的相关面试题,从基础概念到高级应用进行详尽...
在Java编程语言中,`HashMap` 是一个常用的集合类,用于存储键值对。它提供了快速的插入、删除和查找操作,但是不保证元素的顺序。然而,如果需要按照特定规则进行排序,我们可以使用 `TreeMap` 类。`TreeMap` 是...
Java集合系列之HashMap源码分析 Java集合系列之HashMap源码分析是Java集合系列中的一篇非常重要的文章,它详细介绍了Java集合系列之HashMap源码,具有很高的参考价值。下面我们将对HashMap源码进行详细的分析。 ...
HashSet 和 HashMap 之间有很多相似之处,对于 HashSet 而言,系统采用 Hash 算法决定集合元素的存储位置,这样可以保证能快速存、取集合元素;对于 HashMap 而言,系统 key-value 当成一个整体进行处理,系统总是...
在Java编程语言中,HashMap是集合框架中一个重要的类,用于存储键值对的数据结构。面试中,HashMap的源码分析与实现是一个常见的考察点,因为它涉及到数据结构、并发处理和性能优化等多个核心领域。本篇文章将深入...
5. 取得键值对集合:使用 entrySet() 方法取得 HashMap 中的所有键值对,并将其转换为集合。 HashMap 的遍历 1. 使用迭代器遍历:使用 iterator() 方法取得 HashMap 的迭代器,然后使用 hasNext() 和 next() 方法...
Java基础-模拟HashMap集合(基于数组和链表) 在本文中,我们将详细介绍如何模拟Java的HashMap集合,使用数组和链表来实现Hash表的存储。我们将从基本概念开始,逐步深入到HashMap的实现细节中。 什么是HashMap? ...
HashMap是Java集合框架的一个重要组成部分,它实现了Map接口,能够存储键值对映射。在Java编程语言中,最基本的数据结构有两种:数组和引用(模拟指针)。所有的复杂数据结构都可以基于这两种基本结构构建出来,...