java jdk17 HashMap解读

类描述

基于Hash表的Map接口实现。此实现提供了所有的可选的map操作,并且避免了null值和null键。(HashMap类大体上等价于Hashtable,除了它是非同步的和禁止null)。此类不保证map的顺序。特别是,不保证随着时间的变化顺序保持不变。

假设hash功能将元素完全在bucket之中打散,对于基本操作(get和put)此实现提供了常量时间的性能。在集合视图上进行迭代要求的时间与HashMap实例的“容量”(bucket的数量)加上它的大小(key-value映射的数量)成正比。因此,如果迭代性能比较重要,不要设置初始容量太高(或者负载因子太低)是非常重要的。

一个HashMap实例有两个参数影响它的性能:初始容量和负载因子。容量是在哈希表中bucket的数量,并且初始容量就是hash表创建时的容量。负载因子是衡量hash表在容量自动增加之前允许其达到多满的指标。当hash表中条目的数量超过当前容量和负载因子的乘积,hash表重新rehash(重新hash,也就是说,内部数据结构被重建)所以hash表大约有两倍的bucket数量。

一般情况下,默认的负载因子(0.75)在时间和空间消耗之间有很好的平衡。较高的值降低了空间负载但是增加了查询消耗(在大多数的HashMap类的操作上都会影响,包括get和put)。当设置初始容量时应该考虑map中期望的条目数量和它的负载因子,从而最小化重hash操作的数量。如果初始容量大于条目最大值数量除以负载因子,不会有重hash操作发生。

如果许多映射已经在HashMap实例中存储,使用足够大的容量创建它将允许比在随着table的增长所需让他执行自动化重刷更加高效的映射存储。注意,使用许多相同的hashcode()的key肯定会降低任何hash表的性能。为减轻影响,当key是Comparable的时,类可以使用键之间的比较顺序来打破并列。

注意此实现不是同步的。 如果多个线程并发访问hash表,至少有一个线程修改了map结构,他必须在外部同步。(一个结构化的修改可以是添加或者删除一个或者更多映射的任意操作;仅仅修改已经在实例中存在的key的关联值不是结构修改)。这通常是通过一些自然封装了map的对象上同步来完成的。如果没有这样的对象存在,此map应该使用Collections.synchronizedMap方法封装。最好在创建时完成,避免意外的对map的访问:

Map m = Collections.synchronizedMap(new HashMap(...));

所有类的集合视图方法返回的迭代器是快速失败的:如果在创建迭代器之后,在任何时间结构被修改,使用除了迭代器自己的remove方法的任何方法,迭代器将抛出ConcurrentModificationException。这样,在面对并发修改,迭代器很快和干净地失败,而不是冒着在未来任意时间段任意,不确定行为的风险。

注意,迭代器快速失败的行为是无法保证的,通常来说,存在不同步的并发修改时,不可能作出任何硬保证。快速失败尽最大努力抛出ConcurrentModificationException。因此,依赖这个异常写一个程序保证正确性是错误的:迭代器的快速失败行为应该仅用于检测bug。

此类是Java集合框架成员。

实现注意事项

这个map通常扮演的是一个带有槽位的hash表,但是当槽位太大时,他们将被转为TreeNode的槽位,每一个结构类似于java.util.TreeMap。大多数方法使用普通的槽位,但在适时时转换为TreeNode方法(仅仅通过instanceof一个节点检查)。TreeNode的槽位就想其他任意节点遍历和使用,但是当槽位过多时,支持更快速的查找。但是,因为大多数在正常使用的槽位没有过多使用,因此在使用表方法的过程中,对tree槽位的检查可能存在延迟。

tree槽位(即,所有的元素是TreeNode的槽位)主要是按照hashCode排序,但是在tie的情况下,如果两个元素有相同的“class C implements Comparable”,然后使用他们的compareTo方法排序(我们通过反射检查泛型类型来进行验证–可以看方法comparableClassFor)。当键具有不同的哈希值或可排序时,树槽位增加的复杂性值得提供最差情况下O(log n)的操作。因此,当hashCode()方法意外或恶意使用时,性能会优雅地下降,比如返回的值分布不均匀,或者许多键共享一个哈希码,只要它们是可比较的。(如果两者都不适用,与不采取预防措施相比,我们可能会浪费大约2倍的时间和空间。但唯一已知的案例来自于糟糕的用户编程实践,这些实践已经非常缓慢,以至于没有什么区别。)

因为TreeNode大概是常规节点的两倍,我们只有在槽位足够多的节点时才使用(请查看TREEIFY_THRESHOLD)。当他们变得比较小时(由于移除或者resize)他们会被转换成普通的槽位。分布较好的用户hashCode在使用中,tree bins(tree的槽位)是很少使用的。理想情况下,在随机hashCode下,在槽位中节点的频率遵循泊松(Poisson)分布,在默认的重新设置大小阈值为0.75时,参数平均为0.5,尽管由于调整粒度的不同,方差很大。忽略方差,k列表大小的期望出现次数是(exp(-0.5) * pow(0.5, k) / factorial(k)),第一个值是:
0: 0.60653066
1: 0.30326533
2: 0.07581633
3: 0.01263606
4: 0.00157952
5: 0.00015795
6: 0.00001316
7: 0.00000094
8: 0.00000006
更多: 小于千万分之一

tree槽位的根节点通常是他的第一个节点。但是,有时(目前仅在Iteraotr.remove上执行),根可以在其他地方,但可以在父连接之后回复(方法TreeNode.root())。

所有适当的内部方法接收一个hash code作为参数(通常由公共方法提供),允许他们互相调用,无需重新计算用户hashCode。大多数内部方法也接受“tab”参数,即当前的table,当重新整理大小或者转换时,可能是新的也有可能是老的。

当槽位已经变为tree,分隔,非tree的情况下,我们保留他们相同的相对访问/遍历顺序(即字段Node.next)以便更好的保持局部性,并略微简化调用对Iterator.remove的拆分和遍历的处理。当在插入使用比较器时,为了在重新平衡时保持总的排序(尽可能接近),我们比较类和identityHashCodes作为决定因素。

由于子类LinkedHashMap的存在,普通模式和树模式之间的使用和转换变得复杂。下面是定义在插入、删除和访问时调用的挂钩方法,这些方法允许LinkedHashMap内部保持独立于这些机制。(这也需要将一个map实例传递给一些可以创建新节点的工具方法。)

类似于基于ssa的并发编程风格有助于避免所有弯曲指针操作中的混叠错误。

静态变量,关于容量设计的字段

/**
* 默认的初始容量,必须是2的幂
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 // 也就是16
/**
* 最大值容量,如果任意一个带参数的构造器隐式指定,则使用此值,必须是2的幂
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 当在构造器中没有指定时使用的负载因子
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* 使用树而不是列表的bin数量阈值。当元素添加到一个槽位时至少这些节点bins才会转为树。
* 该值必须大于2,且至少为8,以符合tree移除时关于收缩后转换为普通bins的假设。
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* 在resize期间非树化一个bin的bin数量阈值。
* 应该小于TREEIFY_THRESHOLD,至多为6,与移除操作下收缩检测配合。
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* 哪个bin可能被树化的最小table容量。
* (否则如果一个bin中太多节点table将resize)
* 应该至少4 * TREEIFY_THRESHOLD来避免resize和树化阈值之间的冲突。
*/
static final int MIN_TREEIFY_CAPACITY = 64;

内部类-节点

    /*** 基本的哈希bin节点,用于大多数条目(看下面了解* TreeNode子类, 和LinkedHashMap的Entry子类)*/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;return o instanceof Map.Entry<?, ?> e&& Objects.equals(key, e.getKey())&& Objects.equals(value, e.getValue());}}

静态工具-内部方法

    /*** 计算key.hashCode()并将其hash的高位与地位对齐(XOR方式). * 因为table使用了二幂码,所以在当前掩码只有几bit变化的hash集总是冲突。* (在已知的示例中,一组Float键在小表中保持连续的整数。)  * 所以我们提供了一个转换,将高比特的影响向下扩散。在比特传播的速度,实用和质量之间进行权衡。 * 因为许多常用的hash集已经合理分布(不会从传播中获益), * 并且因为我们在bin中使用了tree来处理大的冲突集, 我们只是使用最廉价的方式对一些偏移bit进行异或来减少系统损失, 并结合了高位的影响,否则由于表边界的原因在索引计算中,高位不会被使用。*/static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
    /*** 如果x是"class C implements* Comparable<C>"格式,返回x的Class,否则返回null。*/static Class<?> comparableClassFor(Object x) {if (x instanceof Comparable) {Class<?> c; Type[] ts, as; ParameterizedType p;if ((c = x.getClass()) == String.class) // bypass checksreturn c;if ((ts = c.getGenericInterfaces()) != null) {for (Type t : ts) {if ((t instanceof ParameterizedType) &&((p = (ParameterizedType) t).getRawType() ==Comparable.class) &&(as = p.getActualTypeArguments()) != null &&as.length == 1 && as[0] == c) // type arg is creturn c;}}}return null;}
    /*** Returns k.compareTo(x) if x matches kc (k's screened comparable* class), else 0.* 如果x匹配kc则返回k.compareTo(x)(k是筛选过的可比较类),否则返回0.*/@SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparablestatic int compareComparables(Class<?> kc, Object k, Object x) {return (x == null || x.getClass() != kc ? 0 :((Comparable)k).compareTo(x));}
    /*** 根据给定目标的容量返回2的幂大小,往上ceil进位(5变为8)*/static final int tableSizeFor(int cap) {int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}

内部字段

     /*** 在第一次使用时初始化,当必要时resize。 当分配时,长度一直是2的幂。* (我们也容许在一些启动机制并不需要的操作中长度为0)。*/transient Node<K,V>[] table;/*** 持有缓存的entrySet(). 注意AbstractMap字段用于keySet()和values().*/transient Set<Map.Entry<K,V>> entrySet;/*** 在此map中包含的key-value映射的数量。*/transient int size;/*** HashMap已经被结构化修改的次数的数量。* 结构化修改就是那些HashMap中改变映射的数量或者他的内部结构的修改(比如,rehash)。* 此字段用于使在HashMap的集合视图快速失败(请查看ConcurrentModificationException)。*/transient int modCount;/*** 要进行resize(capacity * load factor)的阈值.* @serial*/int threshold;/*** hash表的负载因子* @serial*/final float loadFactor;

公共方法

    /*** 构造一个带有指定的初始容量和负载因子的空的HashMap* @param  initialCapacity 初始容量* @param  loadFactor      负载因子* @throws IllegalArgumentException 如果初始容量是负数或者负载因子是非正数*/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);}/*** 构造一个带有指定初始容量并且默认的负载因子(0.75)的空的HashMap。* @param  initialCapacity 初始容量.* @throws IllegalArgumentException 如果初始容量是负数.*/public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}/*** Constructs an empty {@code HashMap} with the default initial capacity* (16) and the default load factor (0.75).*/public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted}/*** 构造一个与指定的Map相同的映射的HashMap。HashMap使用默认的负载因子创建。HashMap使用默认的负载因子创建(0.75)并且初始容量满足指定Map的映射。** @param   m 将映射放置到此map的map* @throws  NullPointerException 如果指定的map为空*/public HashMap(Map<? extends K, ? extends V> m) {this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false);}/*** 实现Map.putAll和Map构造器* @param m the map* @param evict 当初始化构造此map时为false,其他则为true(与方法afterNodeInsertion相关).*/final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {int s = m.size();if (s > 0) {if (table == null) { // pre-sizefloat ft = ((float)s / loadFactor) + 1.0F;int t = ((ft < (float)MAXIMUM_CAPACITY) ?(int)ft : MAXIMUM_CAPACITY);if (t > threshold)threshold = tableSizeFor(t);} else {// 由于linked-list桶的限制,我们不能一次全部扩展,但是可以通过现在比以前的重复加倍// 减少重新扩容的工作量。while (s > threshold && table.length < MAXIMUM_CAPACITY)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);}}}/*** 返回此map中key-value映射的数量* @return 此map中key-value映射的数量*/public int size() {return size;}/*** 如果此map没有包含key-value映射返回true* @return 如果此map没有包含key-value映射返回true*/public boolean isEmpty() {return size == 0;}/*** 返回指定key映射的值,如果map没有包含此key的映射则返回null。** 正式来说, 如果此映射中包含键k到值v的映射以至于(key==null ? k==null :* key.equals(k))然后此方法返回v;否则返回null.  (最多只有一个这样的映射.)* null的返回值并不是表示映射中不包含此key的映射;它也可能此映射显性地将此key映射为null。* containKey操作可以用于识别这两种情况。* @see #put(Object, Object)*/public V get(Object key) {Node<K,V> e;return (e = getNode(key)) == null ? null : e.value;}/*** 实现Map.get和相关方法。* @param key the key* @return the node, or null if none*/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 &&(first = tab[(n - 1) & (hash = hash(key))]) != null) {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;}/*** 如果此映射包含指定key的映射则返回true。* @param   key 在此map中测试是否存在的key* @return  如果此映射包含指定key的映射则返回true。* key.*/public boolean containsKey(Object key) {return getNode(key) != null;}/*** 在此map中使用指定的key关联指定的值。如果此map先前包含此key的值,则老值被替换。* @param key 要与指定的value关联的key* @param value 要与指定的key关联的value* @return 此前与此key关联的value,如果没有key的映射则返回null。如果返回null也可能表示此前关联了null。*/public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}/*** 实现Map.put和关联方法。* @param hash key的hash* @param key the key* @param value 要放入的value* @param onlyIfAbsent 如果是true,不会修改存在的值* @param evict 如果是false,table在创建中* @return 先前的值,如果没有则返回null*/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;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 1sttreeifyBin(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 keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}/*** 初始化或者翻倍table大小。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) {// 超过最大值则不在扩容if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}// 新容量翻倍,阈值翻倍else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold}// 老容量小于0,阈值大于0,则新容量等于老阈值else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;else {               // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}// 重新计算阈值if (newThr == 0) {float ft = (float)newCap * loadFactor;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// 将此链表分为2个链表,要么在原位置,要么原位置+oldCap,//其实e.next==null里面的重新放置也可以使用此方法判断//https://segmentfault.com/a/1190000015812438地址有详细介绍Node<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.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;}/*** 将所给定的hash的索引的bin中的链表节点替换*/final void treeifyBin(Node<K,V>[] tab, int hash) {int n, index; Node<K,V> e;// 如果小于最小树化大小则resizeif (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)resize();else if ((e = tab[index = (n - 1) & hash]) != null) {TreeNode<K,V> hd = null, tl = null;do {// 创建tree节点TreeNode<K,V> p = replacementTreeNode(e, null);if (tl == null)hd = p;else {p.prev = tl;tl.next = p;}tl = p;} while ((e = e.next) != null);if ((tab[index] = hd) != null)// 生成树hd.treeify(tab);}}/*** Copies all of the mappings from the specified map to this map.* These mappings will replace any mappings that this map had for* any of the keys currently in the specified map.* 将指定的map的所有映射复制到此map。这些映射将替换在此map中有的在并且在指定map中已经存在的任何映射。* @param m 在此map中将要存储的映射* @throws NullPointerException if the specified map is null*/public void putAll(Map<? extends K, ? extends V> m) {putMapEntries(m, true);}/*** 如果指定的key存在的话,从此map中移除映射。* @param  key 从此map中将要移除映射的key* @return the previous value associated with {@code key}, or*         {@code null} if there was no mapping for {@code key}.*         (A {@code null} return can also indicate that the map*         previously associated {@code null} with {@code key}.)*/public V remove(Object key) {Node<K,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value;}/*** 实现Map.remove和相关方法。* @param hash key的hash* @param key the key* @param value 如果matchValue等于true,则用于匹配的值,否则忽略* @param matchValue 如果为true,只有value相等才移除。* @param movable 如果为false,移除时不能移除其他节点。* @return the node, or null if none*/final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {Node<K,V>[] tab; Node<K,V> p; int n, index;if ((tab = table) != null && (n = tab.length) > 0 &&(p = tab[index = (n - 1) & hash]) != null) {Node<K,V> node = null, e; K k; V v;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))node = p;else if ((e = p.next) != null) {if (p instanceof TreeNode)node = ((TreeNode<K,V>)p).getTreeNode(hash, key);else {do {if (e.hash == hash &&((k = e.key) == key ||(key != null && key.equals(k)))) {node = e;break;}p = e;} while ((e = e.next) != null);}}if (node != null && (!matchValue || (v = node.value) == value ||(value != null && value.equals(v)))) {if (node instanceof TreeNode)((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);else if (node == p)tab[index] = node.next;elsep.next = node.next;++modCount;--size;afterNodeRemoval(node);return node;}}return null;}/*** 移除此表的所有映射。在此调用返回后map将是空的。*/public void clear() {Node<K,V>[] tab;modCount++;if ((tab = table) != null && size > 0) {size = 0;for (int i = 0; i < tab.length; ++i)tab[i] = null;}}/*** 如果map映射一个或者多个key到指定的值则返回true。* @param value 在此map中测试是否存在的值* @return 如果map映射一个或者多个key到指定的值则返回true* */public boolean containsValue(Object value) {Node<K,V>[] tab; V v;if ((tab = table) != null && size > 0) {for (Node<K,V> e : tab) {for (; e != null; e = e.next) {if ((v = e.value) == value ||(value != null && value.equals(v)))return true;}}}return false;}/*** 返回此map中包含的key集合视图。集合是通过map来支持的,所以map的修改是反应到set,反之亦然。* 如果在集合在迭代的过程中(除了迭代器本身的移除操作),map被修改那么结果是未定义的。* 此set支持元素移除,其移除响应的map的映射,通过Iterator.remove,Set.remove,removeAll,retainAll,clear操作。他不支持add或者addAll操作。* @return 包含在此map中的key集合视图*/public Set<K> keySet() {Set<K> ks = keySet;if (ks == null) {ks = new KeySet();keySet = ks;}return ks;}/*** 准备Collection#toArray(Object[])实现的数组。* 如果提供的数组小于map的大小,分配一个新的数组。* 如果提供的数组大于map的大小,null将写在size大小的索引上。* @param 传入到toArray()方法的一个原始数组。* @param <T> 数组元素类型。* @return 一个准备从toArray()方法填充和返回的数组。*/@SuppressWarnings("unchecked")final <T> T[] prepareArray(T[] a) {int size = this.size;if (a.length < size) {return (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);}if (a.length > size) {a[size] = null;}return a;}/*** 使用此map的key填充数组并返回它。此方法假设输入的数组是足够大来填充所有的key。使用prepareArray(Object[])来确认此事。* @param a 一个要填充的数组* @param <T> 数组元素的类型* @return 提供的数组*/<T> T[] keysToArray(T[] a) {Object[] r = a;Node<K,V>[] tab;int idx = 0;if (size > 0 && (tab = table) != null) {for (Node<K,V> e : tab) {for (; e != null; e = e.next) {r[idx++] = e.key;}}}return a;}/*** 使用map的值填充到一个数组并返回它。此方法假设输入的数组足够大来满足所有的值。使用prepareArray(Object[])来确认此事。* @param a 一个要填充的数组* @param <T> 数组元素的类型* @return 提供的数组*/<T> T[] valuesToArray(T[] a) {Object[] r = a;Node<K,V>[] tab;int idx = 0;if (size > 0 && (tab = table) != null) {for (Node<K,V> e : tab) {for (; e != null; e = e.next) {r[idx++] = e.value;}}}return a;}// 专门存放KeySet视图的内部类。final class KeySet extends AbstractSet<K> {public final int size()                 { return size; }public final void clear()               { HashMap.this.clear(); }public final Iterator<K> iterator()     { return new KeyIterator(); }public final boolean contains(Object o) { return containsKey(o); }public final boolean remove(Object key) {return removeNode(hash(key), key, null, false, true) != null;}public final Spliterator<K> spliterator() {return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);}public Object[] toArray() {return keysToArray(new Object[size]);}public <T> T[] toArray(T[] a) {return keysToArray(prepareArray(a));}public final void forEach(Consumer<? super K> action) {Node<K,V>[] tab;if (action == null)throw new NullPointerException();if (size > 0 && (tab = table) != null) {int mc = modCount;for (Node<K,V> e : tab) {for (; e != null; e = e.next)action.accept(e.key);}if (modCount != mc)throw new ConcurrentModificationException();}}}/*** 返回在此map中包含的value的Collection视图。此集合是依据此map的,所以对map的修改会反应到此集合中,反之依然。* 如果当此集合在迭代过程中map被修改(除了通过迭代器本身remove操作),迭代的结果是不确定的。* 集合支持元素移除,也会移除相应的map映射,通过Iterator.remove,Set.remove,removeAll,retainAll,clear操作。他不支持add或者addAll操作。* @return 一个在此map中包含的值的视图*/public Collection<V> values() {Collection<V> vs = values;if (vs == null) {vs = new Values();values = vs;}return vs;}// 专门存放value值的内部类final class Values extends AbstractCollection<V> {public final int size()                 { return size; }public final void clear()               { HashMap.this.clear(); }public final Iterator<V> iterator()     { return new ValueIterator(); }public final boolean contains(Object o) { return containsValue(o); }public final Spliterator<V> spliterator() {return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);}public Object[] toArray() {return valuesToArray(new Object[size]);}public <T> T[] toArray(T[] a) {return valuesToArray(prepareArray(a));}public final void forEach(Consumer<? super V> action) {Node<K,V>[] tab;if (action == null)throw new NullPointerException();if (size > 0 && (tab = table) != null) {int mc = modCount;for (Node<K,V> e : tab) {for (; e != null; e = e.next)action.accept(e.value);}if (modCount != mc)throw new ConcurrentModificationException();}}}/*** 返回一个在此map中包含的映射视图。* 此集合通过map支持,所以对图的修改会反应到set,反之依然。* 如果在set迭代处理的过程中map被修改(除了迭代器自身的remove操作或者通过迭代器返回的map条目上的setValue操作)。迭代的结果是不确定的。* 集合支持元素移除,也会移除相应的map映射,通过Iterator.remove,Set.remove,removeAll,retainAll,clear操作。他不支持add或者addAll操作。* @return 一个此map中包含的映射的set视图。*/public Set<Map.Entry<K,V>> entrySet() {Set<Map.Entry<K,V>> es;return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;}// 专门存放EntrySet的方法final class EntrySet extends AbstractSet<Map.Entry<K,V>> {public final int size()                 { return size; }public final void clear()               { HashMap.this.clear(); }public final Iterator<Map.Entry<K,V>> iterator() {return new EntryIterator();}public final boolean contains(Object o) {if (!(o instanceof Map.Entry<?, ?> e))return false;Object key = e.getKey();Node<K,V> candidate = getNode(key);return candidate != null && candidate.equals(e);}public final boolean remove(Object o) {if (o instanceof Map.Entry<?, ?> e) {Object key = e.getKey();Object value = e.getValue();return removeNode(hash(key), key, value, true, true) != null;}return false;}public final Spliterator<Map.Entry<K,V>> spliterator() {return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);}public final void forEach(Consumer<? super Map.Entry<K,V>> action) {Node<K,V>[] tab;if (action == null)throw new NullPointerException();if (size > 0 && (tab = table) != null) {int mc = modCount;for (Node<K,V> e : tab) {for (; e != null; e = e.next)action.accept(e);}if (modCount != mc)throw new ConcurrentModificationException();}}}

JDK8 Map扩展方法的重写

    // 如果通过key没获取到值,则使用defaultValue@Overridepublic V getOrDefault(Object key, V defaultValue) {Node<K,V> e;return (e = getNode(key)) == null ? defaultValue : e.value;}// 如果不存在才会put@Overridepublic V putIfAbsent(K key, V value) {return putVal(hash(key), key, value, true, true);}// 移除key并且value等于此时key所对应的value@Overridepublic boolean remove(Object key, Object value) {return removeNode(hash(key), key, value, true, true) != null;}// 新值替换老值// 1.必须通过key查询到Node,否则返回true,查询不到并不会替换// 2.查询到的Node的value必须和老值相等@Overridepublic boolean replace(K key, V oldValue, V newValue) {Node<K,V> e; V v;if ((e = getNode(key)) != null &&((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {e.value = newValue;afterNodeAccess(e);return true;}return false;}// 新值替换老值// 1.必须通过key查询到Node,否则返回true,查询不到并不会替换// 2.不会检查查询到的Node的value@Overridepublic V replace(K key, V value) {Node<K,V> e;if ((e = getNode(key)) != null) {V oldValue = e.value;e.value = value;afterNodeAccess(e);return oldValue;}return null;}/*** 如果此方法检测在计算期间映射函数修改此map此方法,将尽力抛出一个ConcurrentModificationException。* @throws ConcurrentModificationException 如果它检测到映射函数修改此map*/// 如果key查询不到Node或者Node的值为空,则通过mappingFunction函数和key进行计算,如果存在则直接返回老值// 将计算出来的value,与key一起存储到Map@Overridepublic V computeIfAbsent(K key,Function<? super K, ? extends V> mappingFunction) {if (mappingFunction == null)throw new NullPointerException();int hash = hash(key);Node<K,V>[] tab; Node<K,V> first; int n, i;int binCount = 0;TreeNode<K,V> t = null;Node<K,V> old = null;if (size > threshold || (tab = table) == null ||(n = tab.length) == 0)n = (tab = resize()).length;if ((first = tab[i = (n - 1) & hash]) != null) {if (first instanceof TreeNode)old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);else {Node<K,V> e = first; K k;do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k)))) {old = e;break;}++binCount;} while ((e = e.next) != null);}V oldValue;if (old != null && (oldValue = old.value) != null) {afterNodeAccess(old);return oldValue;}}int mc = modCount;V v = mappingFunction.apply(key);if (mc != modCount) { throw new ConcurrentModificationException(); }if (v == null) {return null;} else if (old != null) {old.value = v;afterNodeAccess(old);return v;}else if (t != null)t.putTreeVal(this, tab, hash, key, v);else {tab[i] = newNode(hash, key, v, first);if (binCount >= TREEIFY_THRESHOLD - 1)treeifyBin(tab, hash);}modCount = mc + 1;++size;afterNodeInsertion(true);return v;}/*** 如果此方法检测在计算期间映射函数修改此map此方法,将尽力抛出一个ConcurrentModificationException。* @throws ConcurrentModificationException 如果它检测到映射函数修改此map*/// 如果存在,则通过remappingFunction将key和value重算,并替换老值,并返回新值// 不存在返回null@Overridepublic V computeIfPresent(K key,BiFunction<? super K, ? super V, ? extends V> remappingFunction) {if (remappingFunction == null)throw new NullPointerException();Node<K,V> e; V oldValue;if ((e = getNode(key)) != null &&(oldValue = e.value) != null) {int mc = modCount;V v = remappingFunction.apply(key, oldValue);if (mc != modCount) { throw new ConcurrentModificationException(); }if (v != null) {e.value = v;afterNodeAccess(e);return v;}else {int hash = hash(key);removeNode(hash, key, null, false, true);}}return null;}/*** 如果此方法检测在计算期间映射函数修改此map此方法,将尽力抛出一个ConcurrentModificationException。* @throws ConcurrentModificationException 如果它检测到映射函数修改此map*/// 通过key查询node,不管是否存在则通过remappingFunction生成新值,存在则替换,不存在则新增@Overridepublic V compute(K key,BiFunction<? super K, ? super V, ? extends V> remappingFunction) {if (remappingFunction == null)throw new NullPointerException();int hash = hash(key);Node<K,V>[] tab; Node<K,V> first; int n, i;int binCount = 0;TreeNode<K,V> t = null;Node<K,V> old = null;if (size > threshold || (tab = table) == null ||(n = tab.length) == 0)n = (tab = resize()).length;if ((first = tab[i = (n - 1) & hash]) != null) {if (first instanceof TreeNode)old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);else {Node<K,V> e = first; K k;do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k)))) {old = e;break;}++binCount;} while ((e = e.next) != null);}}V oldValue = (old == null) ? null : old.value;int mc = modCount;V v = remappingFunction.apply(key, oldValue);if (mc != modCount) { throw new ConcurrentModificationException(); }if (old != null) {if (v != null) {old.value = v;afterNodeAccess(old);}elseremoveNode(hash, key, null, false, true);}else if (v != null) {if (t != null)t.putTreeVal(this, tab, hash, key, v);else {tab[i] = newNode(hash, key, v, first);if (binCount >= TREEIFY_THRESHOLD - 1)treeifyBin(tab, hash);}modCount = mc + 1;++size;afterNodeInsertion(true);}return v;}/*** 如果此方法检测在计算期间映射函数修改此map此方法,将尽力抛出一个ConcurrentModificationException。* @throws ConcurrentModificationException 如果它检测到映射函数修改此map*/// 通过key查询是否存在node,不存在则将key,value存储,如果存在则将value和oldValue生成newValue与key一起存储。@Overridepublic V merge(K key, V value,BiFunction<? super V, ? super V, ? extends V> remappingFunction) {if (value == null || remappingFunction == null)throw new NullPointerException();int hash = hash(key);Node<K,V>[] tab; Node<K,V> first; int n, i;int binCount = 0;TreeNode<K,V> t = null;Node<K,V> old = null;if (size > threshold || (tab = table) == null ||(n = tab.length) == 0)n = (tab = resize()).length;if ((first = tab[i = (n - 1) & hash]) != null) {if (first instanceof TreeNode)old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);else {Node<K,V> e = first; K k;do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k)))) {old = e;break;}++binCount;} while ((e = e.next) != null);}}if (old != null) {V v;if (old.value != null) {int mc = modCount;v = remappingFunction.apply(old.value, value);if (mc != modCount) {throw new ConcurrentModificationException();}} else {v = value;}if (v != null) {old.value = v;afterNodeAccess(old);}elseremoveNode(hash, key, null, false, true);return v;} else {if (t != null)t.putTreeVal(this, tab, hash, key, value);else {tab[i] = newNode(hash, key, value, first);if (binCount >= TREEIFY_THRESHOLD - 1)treeifyBin(tab, hash);}++modCount;++size;afterNodeInsertion(true);return value;}}//循环,处理key,value@Overridepublic void forEach(BiConsumer<? super K, ? super V> action) {Node<K,V>[] tab;if (action == null)throw new NullPointerException();if (size > 0 && (tab = table) != null) {int mc = modCount;for (Node<K,V> e : tab) {for (; e != null; e = e.next)action.accept(e.key, e.value);}if (modCount != mc)throw new ConcurrentModificationException();}}// key值不变,替换value值public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {Node<K,V>[] tab;if (function == null)throw new NullPointerException();if (size > 0 && (tab = table) != null) {int mc = modCount;for (Node<K,V> e : tab) {for (; e != null; e = e.next) {e.value = function.apply(e.key, e.value);}}if (modCount != mc)throw new ConcurrentModificationException();}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/733253.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不知道吧,腾讯云轻量应用服务器使用有一些限制!

腾讯云轻量应用服务器相对于云服务器CVM是有一些限制的&#xff0c;比如轻量服务器不支持更换内网IP地址&#xff0c;不支持自定义私有网络VPC&#xff0c;内网连通性方面也有限制&#xff0c;轻量不支持CPU内存、带宽或系统盘单独升级&#xff0c;只能整个套餐整体升级&#x…

题目 2021: 坐标排序

题目描述: 请将坐标x,y,z依照以下规则排序&#xff1a; x为第一关键字&#xff0c;当x相同时&#xff0c;依照y&#xff08;第二关键字&#xff09;大小来排序&#xff0c;当y相同时&#xff0c;依照z大小来排序&#xff08;第三关键字&#xff09; 给出了若干坐标&#xff0c…

【AIGC调研系列】大模型的system prompt破解调研

大模型的system prompt破解方法实践主要涉及到prompt工程和提示注入等技术。首先&#xff0c;prompt工程是指通过精心设计prompt&#xff0c;以提高与大模型的交互效率和准确性。这包括了如何清晰地表达任务要求和期望结果[2]&#xff0c;如何有效使用prompt[4]&#xff0c;以及…

Anthropic 公司最新宣布,他们的 AI 聊天机器人模型击败了 OpenAI 的 GPT-4

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Unity Text文本实现滚动跑马灯效果

在一些公告上我们经常会看到文字滚动跑马灯的效果。 那么在Unity上如何实现&#xff1f; 1、首先创建一个Text(或者TextMeshPro)组件&#xff0c;然后输入需要显示的文本内容&#xff0c;如图&#xff1a; 2、编写控制脚本TextRoll.cs&#xff1a; using System.Collections…

【MGR】MySQL Group Replication 监控

目录 17.4 Monitoring Group Replication 17.4.1 Group Replication Server States 17.4.2 The replication_group_members Table 17.4.3 The replication_group_member_stats Table 17.4 Monitoring Group Replication 17.4.1 Group Replication Server States 服务器实例…

钉钉h5应用 globalthis is not defined vite client

钉钉h5应用 globalthis is not defined vite client problem 背景 钉钉h5应用使用 vue3 vite 构建的前端工程 问题 h5页面在pc端浏览器和pc端钉钉打开正常h5页面在移动端钉钉打开异常 页面空白 通过调试工具找到报错信息 globalthis is not defined vite client reason …

ULTRAL SCALE FPGA TRANSCEIVER速率

CPLL支持2-6.25速率 QPLL支持速率 实际使用CPLL最高可以超过这个&#xff0c;QPLL最低也可以低于这个&#xff0c;xilinx留的阈量还是比较大。

数仓开发-2023/2/29

1.简单自我介绍 2.介绍下之前的公司离线数仓项目 3.sql和hivesql区别&#xff1f; 4.sql的执行顺序&#xff1f; 5.hive的优化 6.说下你之前公司来&#xff0c;你的技能层次在每个公司&#xff1f;你怎么评价你的技能&#xff1f; 7.你的之前业务主要是做什么&#xff1f;我说了…

谈一谈mysql的删除操作 DELETE、TRUNCATE和DROP

MySQL中的删除操作可以通过多种语句实现&#xff0c;包括DELETE、TRUNCATE和DROP。具体来看&#xff1a; DELETE&#xff1a; 属于数据库的DML&#xff08;Data Manipulation Language&#xff09;操作语言。 可以删除一行或多行数据&#xff0c;但不会删除表结构。 在InnoD…

这是谁的女儿?其母亲早已红过头了,现在小小年纪的她也爆红网络,没想到吧?

这是谁的女儿&#xff1f;其母亲早已红过头了&#xff0c;现在小小年纪的她也爆红网络&#xff0c;没想到吧&#xff1f; 原来&#xff0c;作母亲的她在红极一时后似乎沉寂了下来&#xff0c;没想到她11岁的女儿近年来也在社交媒体上走红&#xff0c;她为何也成了小网红呢&…

mock项目:

为什么不使用react严格模式 <React.StrictMode><App /></React.StrictMode>,使用严格模式有一些好处&#xff0c;它会执行额外的检查以帮助发现常见的问题&#xff0c;并提供更好的错误和警告信息。 第三方库的兼容性问题&#xff1a;有些第三方库可能不支持…

数字化转型导师坚鹏:大模型的应用实践(金融)

大模型的应用实践 ——开启人类AI新纪元 打造数字化转型新利器 课程背景&#xff1a; 很多企业和员工存在以下问题&#xff1a; 不清楚大模型对我们有什么影响&#xff1f; 不知道大模型的发展现状及作用&#xff1f; 不知道大模型的针对性应用案例&#xff1f; 课程…

C# 高级特性(十一):多线程之async,await

之前使用Thread和Task启动多线程时都会遇到一个麻烦&#xff0c;就是如何反馈结果。在代码里就是如何设计回调函数。如果带界面还得考虑UI线程的问题。 而使用async&#xff0c;await可以达到两个效果。 1 不用设计回调函数&#xff0c;直接按单线程的格式写。 2 不用考虑UI…

【决策树】预测用户用电量

决策树预测用户用电量 文章目录 决策树预测用户用电量  &#x1f449;引言&#x1f48e;一、 数据预处理数据预处理初步数据分析 二、 机器学习算法决策树回归预测用电量决策树模型介绍&#xff1a;回归预测 三、 可视化结果四、 数据分析与结论代码如下 &#x1f449;引言&a…

2024山东国际健康产业博览会·口腔医疗与医疗器械馆

China-DJK山东健博会|第六届中国国际大健康产业博览会5月27-29日在济南盛大举办&#xff1b; 2024第6届中国&#xff08;济南&#xff09;国际大健康产业博览会&#xff08;China-DJK山东健博会&#xff09; The 2024 sixth China (Jinan) International Big Health Industry …

代码随想录算法训练营第四天|24.两两交换链表中的节点、19.删除链表的倒数第N的节点、07.链表相交、142.环形链表II

代码随想录算法训练营第四天|24.两两交换链表中的节点、19.删除链表的倒数第N的节点、07.链表相交、142.环形链表II 24.两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成…

Vue+OpenLayers7入门到实战:OpenLayers7点聚合(聚散点)功能,地图缩小显示聚集数量,点击聚集点散开和地图放大后显示要素图片

返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7入门到实战 前言 本章介绍如何使用OpenLayers7在地图上实现地图点聚合(聚散点)功能,实现地图缩小显示聚集数量,点击聚集点和地图放大后显示要素对应icon图片的功能。 二、依赖和使用 "ol": "7.5.2"…

猫头虎分享已解决Bug || 云服务中断:CloudOutage, CloudProviderError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

markdown页面宽度放宽

变成以上样式 ------------------------------------------------ 然后最后一行加上 #write{ max-width: 90%; } /* 调整源码正文宽度 */ #typora-source .CodeMirror-lines { max-width: 90%; } /* 调整输出 PDF 文件宽度 */ media print { #write{ max-w…