我是风筝,公众号「古时的风筝」,一个兼具深度与广度的程序员鼓励师,一个本打算写诗却写起了代码的田园码农!
文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面。

这是上篇文章 有趣的条漫版 HashMap,25岁大爷都能看懂 的文字版。有不少同学说条漫版的比较有意思,简单易懂,但是毕竟图片画不了那么详细,只能从大面而上理解。

真正的了解细节,还得看这一篇。其实是这篇先写完,然后画了不少图片,所以就写了一篇图片版的。本篇 7000 多字,建议三连呦。

在 Java 中,最常用的数据类型是 8 中基本类型以及他们的包装类型以及字符串类型,其次应该就是 ArrayListHashMap了吧。HashMap存的是键值对类型的数据,其存储和获取的速度快、性能高,是非常好用的一个数据结构,每一个 Java 开发者都肯定用过它。

而且 HashMap的设计巧妙,其结构和原理也经常被拿去当做面试题。其中有很多巧妙的算法和设计,比如 Hash 算法、拉链法、红黑树设计等,值得每一个开发者借鉴学习。

想了老半天,怎么才能简单易懂的把 HashMap说明白呢,那就从我理解它的思路和过程去说吧。要理解一个事物最好的方式就是先了解整体结构,再去追究细节。所以,我们先从结构谈起。

先从结构说起

拿我自身的一个体会来说吧,风筝我作为一个专业路痴,对于迷路这件事儿绝不含糊,虽然在北京混迹多年,但是只在中关村能分清南北,其他地方,哪怕是我每天住的小区、每天工作的公司也分不太清方向,回家只能认一条路,要是打车换条路回家,也得迷糊一阵,这么说吧,在小区前面能回家,小区后面找不到家。去个新地方,得盯着地图看半天。这时,我就在想啊,要是我能在城市上空俯瞰下面的街道,那我就再也不怕找不到回家的路了。这不就是三体里的降维打击吗,站在高维的立场,理解低维的事物,那就简单多了。

理解数据结构也是一个道理,大多数时候,我们都是停留在会用的层面上,理解一些原理也只是支离破碎的,困在数据机构的迷宫里跌跌撞撞,迫切的需要一张地图或者一架直升机。

先来看一下整个 Map家族的集成关系图,一看东西还不少,但其他的可能都没怎么用过,只有 HashMap最熟悉。

image-20200618174439761

以下描述可能不够专业,只为简单的描述 HashMap的结构,请结合下图进行理解。

image-20200615230214687

HashMap主体上就是一个数组结构,每一个索引位置英文叫做一个 bin,我们这里先管它叫做桶,比如你定义一个长度为 8 的 HashMap,那就可以说这是一个由 8 个桶组成的数组。当我们像数组中插入数据的时候,大多数时候存的都是一个一个 Node 类型的元素,Node 是 HashMap中定义的静态内部类。

当插入数据(也就是调用 put 方法)的时候,并不是按顺序一个一个向后存储的,HashMap中定义了一套专门的索引选择算法,叫做散列计算,但散列计算存在一种情况,叫哈希碰撞,也就是两个不一样的 key 散列计算出来的 hash 值是一致的,这种情况怎么办呢,采用拉链法进行扩展,比如图中蓝色的链表部分,这样一来,具有相同 hash 值的不同 key 即可以落到相同的桶中,又保证不会覆盖之前的内容。

但随着插入的元素越来越多,发生碰撞的概率就越大,某个桶中的链表就会越来越长,直到达到一个阈值,HashMap就受不了了,为了提升性能,会将超过阈值的链表转换形态,转换成红黑树的结构,这个阈值是 8 。也就是单个桶内的链表节点数大于 8 ,就会将链表变身为红黑树。

以上概括性的描述就是 HashMap的整体结构,也是我们进一步研究细节的蓝图。我们将从中抽取出几个关键点一一解释,从整体到细节,降维打击 HashMap

接下来就是说明为什么会设计成这样的结构以及从单纯数组到桶内链表产生,接着把链表转换成红黑树的详细过程。

认清几个关键概念

存储容器

因为HashMap内部是用一个数组来保存内容的,数组定义如下:

transient Node<K,V>[] table;

Node 类型

table 是一个 Node类型的数组,Node是其中定义的静态内部类,主要包括 hash、key、value 和 next 的属性。比如之后我们使用 put 方法像其中加键值对的时候,就会转换成 Node 类型。

static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;
}

TreeNode

前面说了,当桶内链表到达 8 的时候,会将链表转换成红黑树,就是 TreeNode类型,它也是 HashMap中定义的静态内部类。

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {TreeNode<K,V> parent;  // red-black tree linksTreeNode<K,V> left;TreeNode<K,V> right;TreeNode<K,V> prev;    // needed to unlink next upon deletionboolean red;
}

容量和默认容量

容量就是 table 数组的长度,也就是我们所说的桶的个数。其定义如下

int threshold;

默认是 16,如果我们在初始化的时候没有指定大小,那就是 16。当然我们也可以自己指定初始大小,而 HashMap 要求初始大小必须是 2 的 幂次方。

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

元素个数

容量是指定了桶的个数,而 size 是说 HashMap中实际存了多少个键值对。

transient int size;

最大容量

table 的长度也是有限制的,不能无限大,HashMap规定最大长度为 2 的30次方。

static final int MAXIMUM_CAPACITY = 1 << 30;

负载因子

这是一个系数,它和 threshold 结合起作用,默认是 0.75。一般情况下不要改。

final float loadFactor;

扩容阈值

阈值 = 容量 x 负载因子,假设当前 HashMap的容量是 16,负载因子是默认值 0.75,那么当 size 到达 16 x 0.75= 12 的时候,就会触发扩容。

初始化 HashMap

使用 HashMap肯定要初始化吧,很多情况下都是用无参构造方法创建。

Map<String,String> map = new HashMap<>();

这种情况下所有属性都是默认值,比如容量是 16,负载因子是 0.75。

另外推荐的一种初始化方式,就是给定一个默认容量,比如指定默认容量是 32。

Map<String,String> map = new HashMap<>(32);

但是 HashMap 要求初始大小必须是 2 的 n 次方,但是又不能要求每个开发人员指定初始容量的时候都按要求来,比如我们指定初始大小为为 7、18 这种会怎么样呢?

没关系,HashMap中有个方法专门负责将传过来的参数值转换为最接近、且大于等于指定参数的 2 的 n 次方的值,比如指定大小为 7 的话,最后实际的容量就是 8 ,如果指定大小为 18的话,那最后实际的容量就是 32 。

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);
}

执行这个转换动作的就是 tableSizeFor方法,经过转换后,将最终的结果赋值给 threshold变量,也就是初始容量,也就是本篇中所说的桶个数。

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;
}

tableSizeFor这个方法就有意思了,先把初始参数减 1,然后连着做或等于无符号右移操作,最后算出一个接近的 2 的幂次方,下图演示了初始参数为 18 时的一系列操作,最后得出的初始大小为 32。

image-20200614232442638

这个算法很有意思了,比如你给的初始大小是 63,那得到的结果就是 64,如果初始大小给定 65 ,那得到的结果就是 128,总是能得出不小于给定初始大小,并且最接近的2的n次方的最终值。

从 put 方法解密核心原理

put方法是增加键值对最常用的方法,也是最复杂的过程,增加键值对的过程涉及了 HashMap最核心的原理,主要包括以下几点:

  1. 什么情况下会扩容,扩容的规则是什么?
  2. 插入键值对的时候如何确定索引,HashMap可不是按顺序插入的,那样不就真成了数组了吗。
  3. 如何确保 key 的唯一性?
  4. 发生哈希碰撞怎么处理?
  5. 拉链法是什么?
  6. 单桶内的链表如何转变成红黑树?

以下是 put 方法的源码,我在其中做了注释。


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) {HashMap.Node<K,V>[] tab; // 声明 Node 数组 tabHashMap.Node<K,V> p;    // 声明一个 Node 变量 pint n, i;/*** table 定义 transient Node<K,V>[] table; 用来存储 Node 节点* 如果 当前table为空,则调用resize() 方法分配数组空间*/if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;// n 总是为 2 的幂次方,(n-1) & hash 可确定 tab.length (也就是table数组长度)内的索引// 然后 创建一个 Node 节点赋给当前索引if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {//如果当前索引位置已经有值了,怎么办// 拉链法出场HashMap.Node<K,V> e;K k;// 判断 key 值唯一性// p 是当前待插入索引处的值// 哈希值一致并且(当前位置的 key == 待插入的key(注意 == 符号),或者key 不为null 并且 key.equals(k))if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k)))) //如果当前节点只有一个元素,且和待插入key一样 则覆盖// 将 p(当前索引)节点临时赋予 ee = p;else if (p instanceof HashMap.TreeNode) // 如果当前索引节点是一颗树节点//插入节点树中 并返回e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {// 当前索引节点即不是只有一个节点,也不是一颗树,说明是一个链表for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) { //找到没有 next 的节点,也就是最后一个// 创建一个 node 赋给 p.nextp.next = newNode(hash, key, value, null);// 如果当前位置+1之后大于 TREEIFY_THRESHOLD 则要进行树化if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st//执行树化操作treeifyBin(tab, hash);break;}//如果又发生key冲突则停止 后续这个节点会被相同的key覆盖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;// 当实际长度大于 threshold 时 resizeif (++size > threshold)resize();afterNodeInsertion(evict);return null;
}

首次初始化数组和扩容

在执行 put方法时,第一步要检查 table 数组是否为空或者长度是否为 0,如果是这样的,说明这是首次插入键值对,需要执行 table 数组初始化操作。

另外,随之键值对添加的越来越多,HashMap的 size 越来越大,注意 size 前面说了,是实际的键值对数量,那么 size 到了多少就要扩容了呢,并不是等 size 和 threshold(容量)一样大了才扩容,而是到了阈值就开始扩容,阈值上面也说了,是容量 x 负载因子

为什么放在一起说呢,因为首次初始化和扩容都是用的同一个方法,叫做 resize()。以下是我注释的 resize()方法。

final HashMap.Node<K,V>[] resize() {// 保存 table 副本,接下来 copy 到新数组用HashMap.Node<K,V>[] oldTab = table;// 当前 table 的容量,是 length 而不是 sizeint oldCap = (oldTab == null) ? 0 : oldTab.length;// 当前桶大小int oldThr = threshold;int newCap, newThr = 0;if (oldCap > 0) { //如果当前容量大于 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}else if (oldThr > 0) // 初始化的场景(给定默认容量),比如 new HashMap(32)newCap = oldThr; //将容量设置为 threshold 的值else {               // 无参数初始化场景,new HashMap()// 容量设置为 DEFAULT_INITIAL_CAPACITYnewCap = 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"})HashMap.Node<K,V>[] newTab = (HashMap.Node<K,V>[])new HashMap.Node[newCap];table = newTab;if (oldTab != null) {for (int j = 0; j < oldCap; ++j) {HashMap.Node<K,V> e;//遍历 获得得到元素 赋给 eif ((e = oldTab[j]) != null) { //如果当前桶不为空oldTab[j] = null; // 置空回收if (e.next == null) //节点 next为空的话 重新寻找落点 newTab[e.hash & (newCap - 1)] = e;else if (e instanceof HashMap.TreeNode) //如果是树节点//红黑树节点单独处理((HashMap.TreeNode<K,V>)e).split(this, newTab, j, oldCap);else { // 保持原顺序HashMap.Node<K,V> loHead = null, loTail = null;HashMap.Node<K,V> hiHead = null, hiTail = null;HashMap.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;
}

首次初始化

put方法中线先检查 table 数组是否为空,如果为空就初始化。

if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;

首次初始化分为无参初始化和有参初始化两种情况,前面在讲 HashMap初始化的时候说了,无参情况默认就是 16,也就是 table 的长度为 16。有参初始化的时候,首先使用 tableSizeFor()方法确定实际容量,最后 new 一个 Node 数组出来。

HashMap.Node<K,V>[] newTab = (HashMap.Node<K,V>[])new HashMap.Node[newCap];

其中 newCap就是容量,默认16或者自定义的。

而这个过程中还有很重要的一步,就是维护扩容阈值

扩容

put方法中,判断当 size(实际键值对个数)到达 threshold (阈值)时,触发扩容操作。

// 当实际长度大于 threshold 时 resize
if (++size > threshold)resize();

HashMap遵循两倍扩容规则,每次扩容之后的大小是扩容前的两倍。另外,说到底,底层的存储还是一个数组,Java 中没有真正的动态数组这一说,数组初始化的时候是多大,那它就一直是这么大,那扩容是怎么来的呢,答案就是创建一个新数组,然后将老数组的数据拷贝过去。

拷贝的时候可能会有如下几种情况:

  1. 如果节点 next 属性为空,说明这是一个最正常的节点,不是桶内链表,也不是红黑树,这样的节点会重新计算索引位置,然后插入。
  2. 如果是一颗红黑树,则使用 split方法处理,原理就是将红黑树拆分成两个 TreeNode 链表,然后判断每个链表的长度是否小于等于 6,如果是就将 TreeNode 转换成桶内链表,否则再转换成红黑树。
  3. 如果是桶内链表,则将链表拷贝到新数组,保证链表的顺序不变。

确定插入点

当我们调用 put方法时,第一步是对 key 进行 hash 计算,计算这个值是为了之后寻找落点,也就是究竟要插入到 table 数组的哪个桶中。

hash 算法是这样的,拿到 key 的 hashCode,将 hashCode 做一次16位右位移,然后将右移的结果和 hashCode 做异或运算,这段代码叫做「扰动函数」,之所以不直接拿 hashCode 是为了增加随机性,减少哈希碰撞次数。

/**
* 用来计算 key 的 hash 值
**/
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

拿到这个 hash 值之后,会进行这样的运算 i = (n - 1) & hash,其中 i就是最终计算出来的索引位置。

有两个场景用到了这个索引计算公式,第一个场景就是 put方法插入键值对的时候。第二个场景是在 resize 扩容的时候,new 出来新数组之后,将已经存在的节点移动到新数组的时候,如果节点不是链表,也不是红黑树,而是一个普通的 Node 节点,会重新计算,找到在新数组中的索引位置。

接着看图,还是图说的清楚。

HashMap 要求容量必须是 2 的 n 次方,2的 n 次方的二进制表示大家肯定都很清楚,2的6次方,就是从右向左 6 个 0,然后第 7 位是 1,下图展示了 2 的 6 次方的二进制表示。

image-20200615181108891

然后这个 n-1的操作就厉害了,减一之后,后面之前二进制表示中 1 后面的 0 全都变成了 1,1 所在的位变为 0。比如 64-1 变为 63,其二进制表示是下面这样的。

image-20200615181859017

下图中,前面 4 行分别列出了当 map 的容量为 8、16、32、64的时候,假设容量为 n,则对应的 n-1 的二进制表示是下面这样的,尾部一片红,都是 1 ,能预感到将要有什么骚操作。

没错,将这样的二进制表示代入这个公式 (n - 1) & hash中,最终就能确定待插入的索引位了。接着看图最下面的三行,演示了假设当前 HashMap的容量为 64 ,而待插入的一个 key 经过 hash 计算后得到的结果是 99 时,代入公式计算 index 的值,也就是 (64-1)& 99,最终的计算结果是 35,也就是这个 key 会落到 table[35] 这个位置。

为什么 HashMap一定要保证容量是 2 的幂次方呢,通过二进制表示可以看出,如果有多位是 1 ,那与 hash 值进行与运算的时候,更能保证最后散列的结果均匀,这样很大程度上由 hash 的值来决定。

image-20200615175605039

如何确保 key 的唯一性

HashMap中不允许存在相同的 key 的,那怎么保证 key 的唯一性呢,判断的代码如下。

if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))

首先通过 hash 算法算出的值必须相等,算出的结果是 int,所以可以用 == 符号判断。只是这个条件可不行,要知道哈希碰撞是什么意思,有可能两个不一样的 key 最后产生的 hash 值是相同的。

并且待插入的 key == 当前索引已存在的 key,或者 待插入的 key.equals(当前索引已存在的key),注意== 和 equals 是或的关系。== 符号意味着这是同一个对象, equals 用来确定两个对象内容相同。

如果 key 是基本数据类型,比如 int,那相同的值肯定是相等的,并且产生的 hashCode 也是一致的。

String 类型算是最常用的 key 类型了,我们都知道相同的字符串产生的 hashCode 也是一样的,并且字符串可以用 equals 判断相等。

但是如果用引用类型当做 key 呢,比如我定义了一个 MoonKey 作为 key 值类型

public class MoonKey {private String keyTile;public String getKeyTile() {return keyTile;}public void setKeyTile(String keyTile) {this.keyTile = keyTile;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;MoonKey moonKey = (MoonKey) o;return Objects.equals(keyTile, moonKey.keyTile);}
}

然后用下面的代码进行两次添加,你说 size 的长度是 1 还是 2 呢?

Map<MoonKey, String> m = new HashMap<>();
MoonKey moonKey = new MoonKey();
moonKey.setKeyTile("1");
MoonKey moonKey1 = new MoonKey();
moonKey1.setKeyTile("1");
m.put(moonKey, "1");
m.put(moonKey1, "2");
System.out.println(hash(moonKey));
System.out.println(hash(moonKey1));
System.out.println(m.size());

答案是 2 ,为什么呢,因为 MoonKey 没有重写 hashCode 方法,导致 moonkey 和 moonKey1 的 hash 值不可能一样,当不重写 hashCode 方法时,默认继承自 Object的 hashCode 方法,而每个 Object对象的 hash 值都是独一无二的。

划重点,正确的做法应该是加上 hashCode的重写。

@Override
public int hashCode() {return Objects.hash(keyTile);
}

这也是为什么要求重写 equals 方法的同时,也必须重写 hashCode方法的原因之一。 如果两个对象通过调用equals方法是相等的,那么这两个对象调用hashCode方法必须返回相同的整数。有了这个基础才能保证 HashMap或者HashSet的 key 唯一。

发生哈希碰撞怎么办

前面刚说了相等的对象产生的 hashCode 也要相等,但是不相等的对象使用 hash方法计算之后也有可能产生相同的值,这就叫做哈希碰撞。虽然通过算法已经很大程度上避免碰撞的发生,但是却无法避免。

产生碰撞之后,自然得出的在 table 数组的索引(也就是桶)也是一样的,这时,怎么办呢,一个桶里怎么放多个键值对?

拉链法

文章刚开头就提到了,HashMap可不是简单的数组而已。当碰撞发生就坦然接收。有一种方法叫做拉链法,不是衣服上那种拉链。而是,当碰撞发生了,就在当前桶上拉一条链表出来,这样解释就合理了。

前面介绍关键概念的时候提到了 Node类型,里面有个属性叫做 next,它就是为了这种链表设计的,如下图所示。node1、node2、node3都落在了同一个桶中,这时候就得用链表的方式处理了,node1.next = node2,node2.next = node3,这样将链表串起来。而 node3.next = null,则说明这是链表的尾巴。

当有新元素准备插入到链表的时候,采用的是尾插法,而不是头插法了,JDK 1.7 的版本采用的是头插法,但是头插法有个问题,就是在两个线程执行 resize() 扩容的时候,很可能造成环形链表,导致 get 方法出现死循环。

image-20200616230957309

链表转换成树

链表不是碰撞处理的终极结构,终极结构是红黑树,当链表长度到达 8 之后,再有新元素进来,那就要开始由链表到红黑树的转换了。方法 treeifyBin是完成这个过程的。

使用红黑树是出于性能方面的考虑,红黑树的查找速度要优于链表。那为什么不是一开始就直接生成红黑树,而是链表长度大于 8 之后才升级成树呢?

首先来说,哈希碰撞的概率还是很小的,大部分情况下都是一个桶装一个 Node,即便发生碰撞,都碰撞到一个桶的概率那就更是少之又少了,所以链表长度很少有机会能到 8 ,如果链表长度到 8 了,那说明当前 HashMap中的元素数量已经非常大了,那这时候用红黑树来提高性能是可取的。而反过来,如果 HashMap总的元素很少,即便用红黑树对性能的提升也不大,况且红黑树对空间的使用要比链表大很多。

get 方法

T value = map.get(key);

例如通过上面的语句通过 key 获取 value 值,是我们最常用到的方法了。

image-20200617141956896

看图理解,当调用 get方法后,第一步还是要确定索引位置,也就是我们所说的桶的位置,方法和 put方法时一样,都是先使用 hash这个 扰动函数 确定 hash 值,然后用 (n-1) & hash获取索引。这不废话吗,当然得和 put的时候一样了,不一样还怎么找到正确的位置。

确定桶的位置后,会出现三种情况:

单节点类型: 也就是这个桶内只有一个键值对,这也在 HashMap中存在最多的类型,只要不发生哈希碰撞都是这种类型。其实 HashMap最理想的情况就是这样,全都是这种类型就完美了。

链表类型: 如果发现 get 的 key 所在的是一个链表结构,就需要遍历链表,知道找到 key 相等的 Node。

红黑树类型: 当链表长度超过 8 就转变成红黑树,如果发现找到的桶是一颗红黑树,就使用红黑树专有的快速查找法查找。

另外,Map.containsKey方法其实用的就是 get方法。

remove 方法

removeputget方法类似,都是先求出 key 的 hash 值,然后 (n-1) & hash获取索引位置,之后根据节点的类型采取不同的措施。

单节点类型: 直接将当前桶元素替换为被删除 node.next ,其实就是 null。

链表类型: 如果是链表类型,就将被删除 node 的前一个节点的 next 属性设置为 node.next。

红黑树类型: 如果是一棵红黑树,就调用红黑树节点删除法,这里,如果节点数在 2~6之间,就将树结构简化为链表结构。

非线程安全

HashMap没有做并发控制,如果想在多线程高并发环境下使用,请用 ConcurrentHashMap。同一时刻如果有多个线程同时执行 put 操作,如果计算出来的索引(桶)位置是相同的,那会造成前一个 key 被后一个 key 覆盖。

比如下图线程 A 和 线程 B 同时执行 put 操作,很巧的是计算出的索引都是 2,而此时,线程A 和 线程B都判断出索引为 2 的桶是空的,然后就是插入值了,线程A先 put 进去了 key1 = 1的键值对,但是,紧接着线程B 又 put 进去了 key2 = 2,线程A 表示痛哭流涕,白忙活一场。最后索引为2的桶内的值是 key2=2,也就是线程A的存进去的值被覆盖了。

image-20200617213357211

总结

前面没说,HashMap搞的这么复杂不是白搞的,它的最大优点就是快,尤其是 get数据,是 O(1)级别的,直接定位索引位置。

HashMap不是单纯的数组结构,当发生哈希碰撞时,会采用拉链法生成链表,当链表大于 8 的时候会转换成红黑树,红黑树可以很大程度上提高性能。

HashMap容量必须是 2 的 n 次方,这样设计是为了保证寻找索引的散列计算更加均匀,计算索引的公式为 (n - 1) & hash

HashMap在键值对数量达到扩容阈值「容量 x 负载因子」的时候进行扩容,每次扩容为之前的两倍。扩容的过程中会对单节点类型元素进行重新计算索引位置,如果是红黑树节点则使用 split方法重新考量,是否将红黑树变为链表。


壮士且慢,先给点个赞吧,总是被白嫖,身体吃不消!

我是风筝,公众号「古时的风筝」。一个兼具深度与广度的程序员鼓励师,一个本打算写诗却写起了代码的田园码农!你可选择现在就关注我,或者看看历史文章再关注也不迟。

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

相关文章

  1. ffmpeg抓取麦克风音频并转为G711格式(C语言)

    一、获取麦克风设备#include <stdio.h> #include <dshow.h> #include <string> #include <Windows.h> #include <comutil.h>#pragma comment(lib, "Strmiids.lib") #pragma comment(lib, "comsuppw.lib")#define MAX_FRIEND…...

    2024/4/15 3:01:43
  2. SSM(Springmvc Spring MyBatis)框架理解

    1、Spring的DI和AOP 1.1 定义 DI定义:DI(Dependency Injection)依赖注入,指容器复制创建和维护对象之间的依赖关系,实现了不同业务层之间的低耦合。 AOP定义:AOP(Aspect Oriented Programming)面向切面编程,可以通过预编译方式和运行期动态代理实现在不修改源代码的情…...

    2024/4/28 3:25:53
  3. M7+M4双核架构MCU,STM32H7将微控制器性能进一步提升

    转载:https://baijiahao.baidu.com/s?id=1639275093141468042&wfr=spider&for=pc作者:刘岩轩M7+M4双核架构MCU,STM32H7将微控制器性能进一步提升21IC中国电子网发布时间:19-07-1711:35爱奇新星(北京)信息科技有限公司官方帐号,优质创作者在微控制器的市场上,双…...

    2024/4/28 5:53:40
  4. 玩腻了小游戏?Paddle手势识别玩转游戏玩出新花样!

    PaddlePaddle实现手势识别玩转吃豆豆!文章目录:1. 手势数据采集2. PaddleX训练模型3. 测试手势识别模型4. 测试游戏种手势控制5. 大功告成~1. 手势数据采集:2. PaddleX训练模型2.1 定义数据集2.2 使用ResNet18训练模型3 测试手势识别模型:4. 测试游戏中手势控制:5. 大功告…...

    2024/4/24 14:54:44
  5. 新手在2020年如何购买比特币

    受国内外新增疫情的影响,15日比特币下探8910位置附近反弹后,价格临近晚间上行之路越发顺畅,收线位置已经到了9300。二日凌晨延续上行走势,价格重新回到9400上方,但受阻于9500位置,目前价格在9417位置。从整体的走势上看,比特币目前是震荡上行,在这个区间内,如果新手想…...

    2024/4/28 15:36:19
  6. PHP 开发直播短视频社交系统

    技术交流:web演示地址: http://www.jinqianlive.com 后台演示地址: http://www.jinqianlive.com/admin 账号 :test 密码: test 安卓下载:https://baoya.lanzous.com/icsdtxa (用手机浏览器打开下载,不要用微信直接下载) IOS 视频演示: 链接:https://pan.baidu.com/s…...

    2024/4/28 5:06:53
  7. 浅谈vue双向绑定

    双向数据绑定, 就是数据层和视图层中的数据同步, 在写入数据时视图层实时的跟着更新, 可以这样描述的:实现mvvm的双向绑定,是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的…...

    2024/4/28 14:58:52
  8. 阻塞与非阻塞、同步与异步

    阻塞与非阻塞: 阻塞:若接受缓冲区的数据未准备好,进程阻塞,释放cpu,等待数据准备完成 非阻塞:应用程序反复轮询接受缓冲区,数据未准备好,返回size == 0 && errno = EAGAIN;进程不会阻塞,依旧可以进行其他工作 总结:阻塞非阻塞都是同步的 同步与异步 同步: 应…...

    2024/4/24 14:54:42
  9. “Talk is cheap, show me the code”你一行代码有多贵?

    平常程序员喜欢说“talk is cheap, show me the code”这句话,可是你知道你敲下的一行代码背后,有多贵? 相信很多程序员不知道,你的领导也不知道。 在CSDN《2019-2020中国开发者调查报告》中,8.5 成开发者月薪在 5 千至 3 万元之间。其中,月薪在 8 千元至 1.7 万元的占 4…...

    2024/4/28 14:54:30
  10. JS高级(4)——线程机制与事件机制

    文章目录线程机制与事件机制进程与线程浏览器内核定时器引发的思考JS是单线程执行的浏览器的事件循环(轮询)模型相关重要概念H5 Web Workers(多线程) 线程机制与事件机制 进程与线程进程(process)程序的一次执行,它占有一片独有的内存空间。 可以通过任务管理器查看线程…...

    2024/4/24 14:54:38
  11. 常用Oracle数据类型

    1、VARCHAR2(size) 可变长度的字符串,其最大长度为 size 个字节;size的最大值是 4000,而最小值是 1;你必须指定一个VARCHAR2 的 size 2、VARCHAR2(size) 可变长度的字符串,依据所选的国家字符集,其最大长度为 size个字符或字节;size 的最大值取决于储存每个字符所需的字节数,其…...

    2024/4/28 7:50:03
  12. 计算机英语讲课笔记(2020-6-23)

    Use it or lose it. 用进废退。 Translation Exercise: 墨写的谎言,无法掩盖血染的事实。 list the keywords: ink, write (wrote, written), lie, cannot(can’t, unable to), cover up, blood, dye, fact build a sentence: The lie written in ink cannot cover up the fac…...

    2024/4/28 7:34:22
  13. 直播是个风口没错,但你是那头猪吗?—浅谈旅游业应该如何利用直播

    相信大家都听过小米创始人雷军的一句名言,“只要站在风口上,猪都能飞起来”。这句话是什么意思呢?结合雷总的发家背景,这句话可以理解为抓住时代红利,顺势而为就能获得成功。就像这句话的字面意思一样,猪原本是不会飞的,只是风太大,把它吹起来了,顺势而已。雷总的小米…...

    2024/4/28 9:05:01
  14. 使用Redis实现切成编程操作

    此工程为Spring boot项目 Redis切成编程 目标:在方法上添加一个注解,实现查询的时候缓存 方法:自定义注解+AOP切面编程 步骤 话不多说,直接上代码演示 第一步 定义注解 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface RedisCache {…...

    2024/4/28 0:49:18
  15. 武汉首所高职毕业生返校

    久别黉门,夏日重逢。6月15日,长江职业学院毕业生开始返校。早上8点不到,党委书记李永健、校长吴昌友等校领导就亲自到校门口迎接毕业生“回家”,各有关部门负责人、相关学院领导也都在校园迎接久别的学生。这是武汉地区经过评估准许学生返校的第一所高职院校。长江职业学院…...

    2024/4/15 3:01:58
  16. 10.1 Matplotlib

    文章目录Matplotlib基本使用基本用法figure图像设置坐标轴调整名字和间隔设置坐标轴边框 .gca()调整坐标轴legend 图例添加图例调整位置和名称Annotation 标注添加注释 annotate添加注释 texttick 能见度(曲线透明度)保存图像画图种类散点图 scatter柱状图 bar生成基本图形加颜…...

    2024/4/28 3:23:54
  17. PTA 计算分段函数[2]

    本题目要求计算下列分段函数f(x)的值:注:可在头文件中包含math.h,并调用sqrt函数求平方根,调用pow函数求幂。输入格式:输入在一行中给出实数x。输出格式:在一行中按“f(x) = result”的格式输出,其中x与result都保留两位小数。输入样例1:10输出样例1:f(10.00) = 3.16输入样…...

    2024/4/28 15:49:11
  18. 学习笔记三 CSS

    CSS深化了解 (盒模型 浮动 定位 ) CSS 布局的三种机制普通流(标准流) 块级元素会独占一行,从上向下顺序排列; 常用元素:div、hr、p、h1~h6、ul、ol、dl、form、table 行内元素会按照顺序,从左到右顺序排列,碰到父元素边缘则自动换行; 常用元素:span、a、i、em等 浮动…...

    2024/4/28 4:49:50
  19. Maven项目使用Checkstyle检查代码

    目录Maven项目使用Checkstyle检查代码idea中配置checkstyle-IDEA插件在Maven项目中配置使用Checkstyle单模块的maven项目多模块的maven项目Jenkins中配置异常参考 Maven项目使用Checkstyle检查代码 Checkstyle可以做到自定义风格的代码检查,这里提供一些使用的例子供参考。 id…...

    2024/4/15 3:01:53
  20. ydui的datetime日期选择组件

    动态赋值,无效,在iOS一直显示2010-01-01,但是在android是正确的,如下: this.datetime3 = this.$moment(new Date()).format(‘YYYY-MM’); 需要在初始化的时候,直接设置默认值。并且添加属性:init-emit=“false”。但是,仍然有点小问题,第一次弹窗选择日期的时候,弹窗…...

    2024/4/16 16:34:47

最新文章

  1. Web漏扫工具OWASP ZAP安装与使用(非常详细)从零基础入门到精通,看完这一篇就够了。

    本文仅用于安全学习使用&#xff01;切勿非法用途。 一、OWASP ZAP简介 开放式Web应用程序安全项目&#xff08;OWASP&#xff0c;Open Web Application Security Project&#xff09;是一个组织&#xff0c;它提供有关计算机和互联网应用程序的公正、实际、有成本效益的信息。…...

    2024/4/28 21:40:11
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 前端 js 经典:字符编码详解

    前言&#xff1a;计算机只能识别二进制&#xff0c;开发语言中数据类型还有数字&#xff0c;字母&#xff0c;中文&#xff0c;特殊符号等&#xff0c;都需要转化成二进制编码才能让技术机识别。 一. 编码方式 ACSLL、Unicode、utf-8、URL 编码、base64 等。 1. ACSLL 对英语…...

    2024/4/21 11:56:37
  4. 谷粒商城实战(008 缓存)

    Java项目《谷粒商城》架构师级Java项目实战&#xff0c;对标阿里P6-P7&#xff0c;全网最强 总时长 104:45:00 共408P 此文章包含第151p-第p157的内容 简介 数据库承担落盘&#xff08;持久化&#xff09;工作 拿map做缓存 这种是本地缓存&#xff0c;会有一些问题 分布…...

    2024/4/27 11:37:38
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/28 13:52:11
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/28 3:28:32
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/28 13:51:37
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/28 15:57:13
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/28 1:22:35
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57