深入解析 HashMap 的 remove() 方法及其相关实现

HashMap 是 Java 中最常用的集合类之一,它提供了高效的键值对存储和检索功能。本文将详细解析 HashMap 的 remove() 方法及其相关的内部实现,包括 removeNode() 和 removeTreeNode() 方法。通过这些方法,我们可以了解 HashMap 如何高效地移除指定的键值对,并在必要时重新平衡红黑树结构。
给出大致流程图,让我们来跟着流程图看源码。
在这里插入图片描述

HashMap remove()方法

    public V remove(Object key) {Node<K,V> e;//hash(key):计算给定键的哈希码,用来确定在HashMap中的位置//v2:key要移除的键//v3:null用于处理批量删除操作中的前一节节点//v4:false表示仅匹配键而不匹配键值,传false表示需要同时匹配键和值//v5:true表示是否移动其他节点以填补空缺,true表示移动节点return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value;}

removeNode()流程图:
在这里插入图片描述在这里插入图片描述

removeNode方法

removeNode 方法是 HashMap 中用于移除指定键值对的核心方法。它负责查找并移除与给定键和值匹配的节点,并在必要时重新平衡红黑树结构。以下是该方法的大致步骤总结:

  • 检查桶数组:
    • 确保桶数组不为空且长度大于0。
    • 计算索引并获取桶位置的头节点。
  • 查找要移除的节点:
    • 检查第一个节点是否是要移除的节点。
    • 如果不是,遍历链表或红黑树查找匹配的节点。
  • 移除节点:
    • 根据节点类型(链表或红黑树)进行相应的移除操作。
    • 更新桶数组或其他节点的引用。
    • 更新 modCount 和 size。
    • 调用 afterNodeRemoval 钩子函数。
    • 返回被移除的节点。
  • 返回结果:
    • 如果没有找到要移除的节点,返回 null。
    /*** * @param hash 键的哈希码* @param key 要移除的键* @param value 要匹配的值* @param matchValue 是否需要匹配匹配值,如果为true,则只有当键和值都匹配时才移除* @param movable 是否允许移动其他节点以填补空缺* @return*/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;//如果当前的桶数组不为空且桶数组的长度大于0且桶数组中的索引位置不为空,执行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;//检查第一个节点是否是要移除的节点,如果哈希码和键都匹配,则将p赋值给nodeif (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))node = p;//如果第一个节点不是要移除的节点,则检查链表中的下一个节点else if ((e = p.next) != null) {if (p instanceof TreeNode)//如果p是一个红黑树节点,则使用getTreeNode方法查找节点node = ((TreeNode<K,V>)p).getTreeNode(hash, key);else {//否则,遍历链表查找节点//do...while循环遍历链表,直到找到匹配的节点或链表结束do {if (e.hash == hash &&((k = e.key) == key ||(key != null && key.equals(k)))) {node = e;break;}p = e;} while ((e = e.next) != null);}}//如果找到了要移除的节点node并且不需要匹配值,或者值夜匹配,则进行移除操作if (node != null && (!matchValue || (v = node.value) == value ||(value != null && value.equals(v)))) {if (node instanceof TreeNode)//如果node是一个红黑树节点,则调用removeTreeNode方法移除节点((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);else if (node == p)//如果node是桶数组中的第一个节点,则更新桶数组中的引用tab[index] = node.next;else//否则更新前一个节点的next引用p.next = node.next;++modCount;//更新修改计数器和大小--size;afterNodeRemoval(node);//调用钩子函数afterNOdeRemoval,用于在节点移除后一些额外的操作return node;//返回被移除的节点}}return null;//如果没有找到要移除的节点,则返回null}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

removeTreeNode()方法

removeTreeNode 方法是 HashMap 中用于从红黑树结构中移除节点的方法。这个方法处理了红黑树的删除操作,包括更新指针、重新平衡树以及在必要时将树转换回链表形式。以下是该方法的主要步骤总结:

  • 检查桶数组:
    • 确保桶数组不为空且长度大于0。
    • 计算索引并获取桶位置的头节点。
  • 更新双向链表引用:
    • 更新前驱节点和后继节点的引用。
  • 找到真正的根节点:
    • 如果 root 的父节点不为空,找到真正的根节点。
  • 检查树的大小:
    • 如果树太小,将其转为链表形式。
  • 获取要移除的节点及其子节点:
    • 根据子节点情况选择替换节点。
  • 处理有两个子节点的情况:
    • 找到后继节点,交换颜色,更新子节点和父节点引用。
  • 处理有一个子节点或没有子节点的情况:
    • 选择合适的替换节点。
  • 更新父节点的引用:
    • 更新父节点的引用,清空被移除节点的子节点和父节点引用。
  • 重新平衡树:
    • 如果被移除的节点是黑色节点,调用 balanceDeletion 方法重新平衡树。
  • 分离被移除的节点:
    • 分离被移除节点与父节点的连接。
  • 移动新的根节点:
    • 在必要时,将新的根节点移动到桶数组的前面。
    final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,boolean movable) {int n;if (tab == null || (n = tab.length) == 0)//如果桶数组tab为空或长度为0,则直接返回return;int index = (n - 1) & hash;//计算键在桶数组中的索引位置//获取该索引位置上的第一个节点first,并将root指向firstTreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;//获取当前节点的前去节点pred和后继结点succTreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;if (pred == null)//如果pred为空,则更新桶数组中的引用tab[index] = first = succ;else//否则更新pred的next引用pred.next = succ;if (succ != null)//如果succ不为空,更新succ的prev引用succ.prev = pred;if (first == null)//如果first为空,则直接返回return;if (root.parent != null)//如果root的父节点不为空,则找到真正的根节点rootroot = root.root();//如果树太小(只有一个节点或两个节点),则将树转为链表形式并返回if (root == null || root.right == null ||(rl = root.left) == null || rl.left == null) {tab[index] = first.untreeify(map);  // too smallreturn;}//获取要移除的节点p及其左右子节点TreeNode<K,V> p = this, pl = left, pr = right, replacement;if (pl != null && pr != null) {//如果p有两个子节点TreeNode<K,V> s = pr, sl;//找到p的后继结点swhile ((sl = s.left) != null) // find successors = sl;//交换p和s的颜色boolean c = s.red; s.red = p.red; p.red = c; // swap colorsTreeNode<K,V> sr = s.right;//获取s的右子节点TreeNode<K,V> pp = p.parent;//获取p的父节点//如果s是p的直接右子节点if (s == pr) { // p was s's direct parentp.parent = s;s.right = p;}else {//否则,调整指针以替换p为sTreeNode<K,V> sp = s.parent;if ((p.parent = sp) != null) {if (s == sp.left)sp.left = p;elsesp.right = p;}if ((s.right = pr) != null)pr.parent = s;}//更新p和s的子节点和父节点p.left = null;if ((p.right = sr) != null)sr.parent = p;if ((s.left = pl) != null)pl.parent = s;if ((s.parent = pp) == null)root = s;else if (p == pp.left)pp.left = s;elsepp.right = s;if (sr != null)//确定替换节点replacement = sr;elsereplacement = p;}else if (pl != null)//如果只有左子节点replacement = pl;else if (pr != null)//如果只有右子节点replacement = pr;else//如果没有子节点replacement = p;if (replacement != p) {//如果替换节点不是p,更新父节点的引用TreeNode<K,V> pp = replacement.parent = p.parent;if (pp == null)root = replacement;else if (p == pp.left)pp.left = replacement;elsepp.right = replacement;p.left = p.right = p.parent = null;}//重新平衡树TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);//分离被移除的节点if (replacement == p) {  // detachTreeNode<K,V> pp = p.parent;p.parent = null;if (pp != null) {if (p == pp.left)pp.left = null;else if (p == pp.right)pp.right = null;}}if (movable)//如果需要,将新的根节点移动到桶数组的前面moveRootToFront(tab, r);}
balanceDeletion()方法

balanceDeletion 方法是 HashMap 中用于在删除红黑树节点后重新平衡树的方法。这个方法通过一系列的旋转和颜色调整来确保红黑树的性质得到保持。以下是该方法的主要步骤总结:

  • 检查基本情况:
    • 如果 x 为空或已经是根节点,直接返回根节点。
    • 如果 x 是新的根节点,将其设为黑色并返回。
    • 如果 x 是红色节点,将其设为黑色并返回根节点。
  • 处理 x 是父节点的左子节点的情况:
    • 根据兄弟节点的颜色和子节点情况进行相应的旋转和颜色调整。
    • 如果需要,更新兄弟节点并继续向上调整。
  • 处理 x 是父节点的右子节点的情况(对称情况):
    • 对称地处理兄弟节点的颜色和子节点情况,进行相应的旋转和颜色调整。
    • 如果需要,更新兄弟节点并继续向上调整。
    static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,TreeNode<K,V> x) {for (TreeNode<K,V> xp, xpl, xpr;;)  {//无限循环,直到找到平衡点或达到根节点if (x == null || x == root)//如果x为空或x已经是根节点,则直接返回根节点return root;else if ((xp = x.parent) == null) {//如果x没有父节点(即x是新的根节点),将x设为黑色并返回xx.red = false;return x;}else if (x.red) {//如果x是红色节点,则将其设为黑色并返回根节点x.red = false;return root;}else if ((xpl = xp.left) == x) {//判断x是否是父节点xp的左子结点if ((xpr = xp.right) != null && xpr.red) {//如果x的兄弟节点xpr存在且是红色//将xpr设为黑色,xp设为红色,并进行左旋xpr.red = false;xp.red = true;root = rotateLeft(root, xp);xpr = (xp = x.parent) == null ? null : xp.right;//更新x的兄弟节点xpr}if (xpr == null)//如果x的兄弟节点xpr不存在x = xp;//将x设置为其父节点xp,继续向上调整else {TreeNode<K,V> sl = xpr.left, sr = xpr.right;//获取xpr的左右子节点if ((sr == null || !sr.red) &&(sl == null || !sl.red)) {//如果xpr的两个子节点都是黑色xpr.red = true;//将xpr设为红色,并将x设置为其父节点xpx = xp;}else {if (sr == null || !sr.red) {//如果xpr的右子节点sr不存在或不是红色if (sl != null)//如果xpr的左子节点s1存在,将其设置为黑色sl.red = false;xpr.red = true;//将xpr设为红色,并对xpr进行右旋root = rotateRight(root, xpr);xpr = (xp = x.parent) == null ?null : xp.right;//更新x的兄弟节点xpr}if (xpr != null) {//如果xpr存在xpr.red = (xp == null) ? false : xp.red;//将xpr的颜色设置为其父节点的颜色if ((sr = xpr.right) != null)//如果xpr的右子节点sr存在,将其设为黑色sr.red = false;}if (xp != null) {//如果xp存在,将其设为黑色,并对xp进行左旋xp.red = false;root = rotateLeft(root, xp);}x = root;//将x设为新的根节点}}}else { // symmetric  对称情况:x是其父节点xp的右子节点if (xpl != null && xpl.red) {//如果x的兄弟节点xpl存在且是红色//将xpl设为黑色,xp设为红色,并进行右旋xpl.red = false;xp.red = true;root = rotateRight(root, xp);xpl = (xp = x.parent) == null ? null : xp.left;//更新x的兄弟节点xpl}if (xpl == null)//如果x的兄弟节点xpl不存在x = xp;//将x设置为其父节点xp,继续向上调整else {TreeNode<K,V> sl = xpl.left, sr = xpl.right;//获取xpl的左右子节点if ((sl == null || !sl.red) &&(sr == null || !sr.red)) {//如果xpl的两个子节点都是黑色xpl.red = true;//将xpl设为红色,并将x设置为其父节点xpx = xp;}else {if (sl == null || !sl.red) {//如果xpl的左子节点sl不存在或不是黑色if (sr != null)//如果xpl的右子节点sr存在,将其设置为黑色sr.red = false;xpl.red = true;//将xpl设为红色,并对xpl进行左旋root = rotateLeft(root, xpl);xpl = (xp = x.parent) == null ?null : xp.left;//更新x的兄弟节点xpl}if (xpl != null) {//如果xpl存在xpl.red = (xp == null) ? false : xp.red;//将xpl的颜色设置为其节点的颜色if ((sl = xpl.left) != null)//如果xpl的左子节点sl存在,将其设为黑色sl.red = false;}if (xp != null) {//如果xp存在,将其设置为黑色,并对xp进行右旋xp.red = false;root = rotateRight(root, xp);}x = root;//将x设置为新的根节点}}}}}
moveRootToFront()

moveRootToFront 方法是 HashMap 中用于将红黑树的根节点移动到桶数组中对应索引位置的第一个节点的方法。这个方法确保了在删除操作后,新的根节点能够被正确地放置在桶数组中的正确位置。以下是该方法的主要步骤总结:

  • 检查输入参数:
    • 确保根节点和桶数组不为空,并获取桶数组的长度。
  • 计算根节点在桶数组中的索引位置:
    • 使用哈希码计算根节点在桶数组中的索引位置。
  • 获取当前索引位置上的第一个节点:
    • 获取索引位置上的第一个节点 first。
  • 检查并移动根节点:
    • 如果根节点不是当前索引位置上的第一个节点,则进行以下操作:
      • 存储根节点的下一个节点 rn。
      • 将根节点设置为索引位置上的第一个节点。
      • 更新根节点及其相邻节点的双向链表引用。
      • 将根节点的前一个节点引用设为 null。
  • 断言检查红黑树的不变性:
    • 调用 checkInvariants(root) 确保红黑树的性质没有被破坏。
    static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {int n;if (root != null && tab != null && (n = tab.length) > 0) {//检查根节点和桶数组是否为空,并获取桶数组的长度int index = (n - 1) & root.hash;//计算根节点在桶数组中的索引位置TreeNode<K,V> first = (TreeNode<K,V>)tab[index];//获取该索引位置上的第一个节点if (root != first) {//如果根节点不是该索引位置上的第一个节点,则进行移动操作Node<K,V> rn;//用于存储根节点的下一个节点tab[index] = root;//将根节点设置为该索引位置上的第一个节点TreeNode<K,V> rp = root.prev;//获取根节点的前一个节点if ((rn = root.next) != null)//如果根节点有下一个节点((TreeNode<K,V>)rn).prev = rp;//更新根节点的下一个节点的前一个节点引用if (rp != null)//如果根节点有前一个节点rp.next = rn;//更新根节点的前一个节点的下一个节点引用if (first != null)//如果原来的第一个节点不为空first.prev = root;//更新原来第一个节点的前一个节点引用,指向新的根节点root.next = first;//更新根节点的下一个节点引用,指向原来的第一个节点root.prev = null;//将根节点的前一个节点引用设为null}assert checkInvariants(root);//断言检查红黑树的不变形}}
rotateLeft()方法

rotateLeft 方法是 HashMap 中用于执行红黑树左旋操作的方法。左旋操作是一种重要的树结构调整方法,它有助于保持红黑树的平衡性和红黑树的性质。以下是该方法的主要步骤总结:

  • 检查输入参数:
    • 确保节点 p 和它的右子节点 r 存在。
  • 更新指针:
    • 定义变量 r、pp 和 rl。
    • 如果 r 的左子节点 rl 存在,更新 rl 的父节点为 p。
    • 更新 r 的父节点为 p 的父节点 pp。
    • 根据 p 是 pp 的左子节点还是右子节点来更新 pp 的相应子节点引用。
    • 将 r 的左子节点设为 p,并将 p 的父节点设为 r。
  • 返回旋转后的根节点:
    • 返回旋转后的根节点 root。
    static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,TreeNode<K,V> p) {TreeNode<K,V> r, pp, rl;// 定义变量 r 为节点 p 的右子节点,pp 为节点 p 的父节点,rl 为节点 p 的右子节点的左子节点if (p != null && (r = p.right) != null) { //检查节点 p 和它的右子节点 r 是否存在if ((rl = p.right = r.left) != null) // 如果 r 存在且 r 的左子节点 rl 存在,则更新 rl 的父节点为 prl.parent = p;if ((pp = r.parent = p.parent) == null)  // 更新 r 的父节点为 p 的父节点 pp(root = r).red = false; // 如果 p 是根节点(即 pp 为空),则将 r 设为新的根节点,并将其颜色设为黑色else if (pp.left == p)// 否则,根据 p 是其父节点 pp 的左子节点还是右子节点来更新 pp 的相应子节点引用pp.left = r;elsepp.right = r;// 将 r 的左子节点设为 p,并更新 p 的父节点为 rr.left = p;p.parent = r;}return root;//返回旋转后的根节点}
rotateRight()方法

rotateRight 方法是 HashMap 中用于执行红黑树右旋操作的方法。右旋操作与左旋操作类似,也是为了保持红黑树的平衡性和红黑树的性质。以下是该方法的主要步骤总结:

  • 检查输入参数:
    • 确保节点 p 和它的左子节点 l 存在。
  • 更新指针:
    • 定义变量 l、pp 和 lr。
    • 如果 l 的右子节点 lr 存在,更新 lr 的父节点为 p。
    • 更新 l 的父节点为 p 的父节点 pp。
    • 根据 p 是 pp 的左子节点还是右子节点来更新 pp 的相应子节点引用。
    • 将 l 的右子节点设为 p,并将 p 的父节点设为 l。
  • 返回旋转后的根节点:
    • 返回旋转后的根节点 root。
    static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,TreeNode<K,V> p) {// 定义变量 l 为节点 p 的左子节点,pp 为节点 p 的父节点,lr 为节点 p 的左子节点的右子节点TreeNode<K,V> l, pp, lr;if (p != null && (l = p.left) != null) { // 检查节点 p 和它的左子节点 l 是否存在if ((lr = p.left = l.right) != null) // 如果 l 存在且 l 的右子节点 lr 存在,则更新 lr 的父节点为 plr.parent = p;if ((pp = l.parent = p.parent) == null)// 更新 l 的父节点为 p 的父节点 pp(root = l).red = false; // 如果 p 是根节点(即 pp 为空),则将 l 设为新的根节点,并将其颜色设为黑色else if (pp.right == p)// 否则,根据 p 是其父节点 pp 的左子节点还是右子节点来更新 pp 的相应子节点引用pp.right = l;elsepp.left = l;// 将 l 的右子节点设为 p,并更新 p 的父节点为 ll.right = p;p.parent = l;}return root;// 返回旋转后的根节点}

通过本文的解析,我们深入了解了 HashMap 的 remove() 方法及其相关实现。HashMap 通过一系列复杂的操作,确保了高效的键值对移除,并在必要时重新平衡红黑树结构。希望本文能够帮助你更好地理解和使用 HashMap。
更多内容,请关注以下公众号
在这里插入图片描述

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

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

相关文章

中国剩余定理 C++

题目 解题思路 原链接&#xff1a;https://www.acwing.com/solution/content/3539/ 大致步骤&#xff1a; 将第2,3,4…n个方程不断与第一个方程合并&#xff0c;得到方程a1k1a2k2m2-m1;用扩展欧几里得算法解出a1k1a2k2gcd(a1, a2)的结果&#xff0c;再将结果扩大(m2-m1)/d倍即…

Linux:进程控制(三)——进程程序替换

目录 一、概念 二、使用 1.单进程程序替换 2.多进程程序替换 3.exec接口 4.execle 一、概念 背景 当前进程在运行的时候&#xff0c;所执行的代码来自于自己的源文件。使用fork创建子进程后&#xff0c;子进程执行的程序中代码内容和父进程是相同的&#xff0c;如果子进…

12.2 Linux_进程间通信_共享内存

概述 什么是共享内存&#xff1a; 共享内存又叫内存映射&#xff0c;可以通过mmap()映射普通文件。 实际上就是将磁盘中的一个文件映射到内存的一个缓冲区中去&#xff0c;这样进程就可以直接将这块空间当作普通内存来访问&#xff0c;不需要再使用I/O中的read/write去访问这…

CV实战01 YOLOv5实现图像分割

网上翻了一天&#xff0c;没找到称心的教程&#xff0c;最后发现还是Ultralytics官方的教程文档好用&#xff01;这里贴上官方教程一起学习&#xff01; 【1&#xff1a;找到官方教程文档】 yolov5官方下载地址&#xff1a;GitHub - ultralytics/yolov5: YOLOv5 &#x1f680…

数字后端零基础入门系列 | Innovus零基础LAB学习Day1

一 Floorplan 数字IC后端设计如何从零基础快速入门&#xff1f;(内附数字IC后端学习视频&#xff09; Lab5-1这个lab学习目标很明确——启动Innovus工具并完成设计的导入。 在进入lab之前&#xff0c;我们需要进入我们的FPR工作目录。 其中ic062为个人服务器账户。比如你端…

多线程代码案例

案例一.单例模式 单例模式是一种设计模式;类似于棋谱,有固定套路,针对一些特定场景可以给出一些比较好的解决方案; 只要按照设计模式来写代码,就可以保证代码不会太差,保证了代码的下限; --------------------------------------------------------------------------------…

【优选算法】(第三十六篇)

目录 ⼆叉树的锯⻮形层序遍历&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 ⼆叉树的最⼤宽度&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 ⼆叉树的锯⻮形层序遍历&#xff08;medium&#xff09; 题目解析 1.题目链接&#xf…

植物大战僵尸杂交版

最新版植物大战僵尸杂交版 最近本款游戏火爆 下载资源如下&#xff1a; win版本&#xff1a;2.3.7 链接&#xff1a;下载地址 提取码&#xff1a;9N3P Mac&#xff08;苹果版本&#xff09;&#xff1a;2.0.0 链接&#xff1a;下载地址 提取码&#xff1a;Bjaa 介绍&#xff…

mysql/doris 计算两个时间相差n天n时n分示范

mysql/doris 计算两个时间相差n天n时n分示范 两个时间&#xff1a;so.create_time&#xff0c;so.update_time CONCAT(FLOOR(DATEDIFF(HOUR ,so.create_time,so.update_time)/24),天,DATEDIFF(HOUR ,so.create_time,so.update_time)%24,时,DATEDIFF(MINUTE ,so.create_time,so…

【重学 MySQL】六十六、外键约束的使用

【重学 MySQL】六十六、外键约束的使用 外键约束的概念关键字主表和从表/父表和子表外键约束的创建条件外键约束的特点外键约束的创建方式外键约束的删除外键约束的约束等级外键约束的级联操作外键约束的示例外键约束的作用开发场景阿里开发规范 在MySQL中&#xff0c;外键约束…

(已解决)vscode使用launch.json进行debug调试报错:Couldn‘t spawn debuggee:embedded null byte

Launch.json 进行debug时报错&#xff1a; 主要原因是vscode全局配置被整乱了&#xff0c;下面是个人解决的方法&#xff0c;以供参考. 在网上也寻找过解决方法&#xff0c;有的说是&#xff0c;在launch.json中&#xff0c;添加一行"python":"/root/miniconda3…

git版本控制软件,操作方法

git版本库操作 1. 注册用户信息 git config --global (邮箱和用户名) 2. 创建工作区 git init 3. 编写文件 vim readme.txt 4. 把文件放到暂存区 git add readme.txt 5. 查看工作区状态 git status 6. 把文件放到本地版本库里 git commit -m "" filename 7. 查看日志…

总结拓展十四:批次管理(2)

1、批次管理后台配置 1.1 批次管理级别配置(T-code:OMTC) ——路径&#xff1a;IMG->后勤-常规->批次管理->指定级别并激活状态管理 1.2 批次状态管理配置(T-code:OMTC) ——路径&#xff1a;IMG->后勤-常规->批次管理->指定级别并激活状态管理 批状态管…

2.1.ReactOS系统NtReadFile函数的实现。

ReactOS系统NtReadFile函数的实现。 ReactOS系统NtReadFile函数的实现。 文章目录 ReactOS系统NtReadFile函数的实现。NtReadFile函数的定义NtReadFile函数的实现 NtReadFile()是windows的一个系统调用&#xff0c;内核中有一个叫NtReadFile的函数 NtReadFile函数的定义 NTS…

【Go初阶】两万字快速入门Go语言

初见golang语法 package mainimport "fmt"func main() {/* 简单的程序 万能的hello world */fmt.Println("Hello Go")} 第一行代码package main定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包&#xff0c;如&#xff1a;package main…

如何捕捉行情爆发的前兆

在金融市场的激烈角逐中&#xff0c;每一次行情的爆发都是投资者获取丰厚回报的关键时刻。然而&#xff0c;如何识别并把握这些时刻&#xff0c;却是一门需要深厚金融专业知识和敏锐洞察力的艺术。今天&#xff0c;我们就来深入探讨行情爆发的初期信号&#xff0c;揭示那些能够…

【Linux】嵌入式Linux系统的组成、u-boot编译

Linux—嵌入式Linux系统的组成、u-boot编译 前言一、嵌入式Linux系统的组成1.1 嵌入式Linux系统和PC完整的操作系统的对比如下&#xff1a;1.2 PC机—Windows系统启动流程&#xff08;PC机—Linux系统、嵌入式ARM—linux系统的启动流程类似&#xff09; 二、编译u-boot2.1 u-bo…

【数据分享】我国第七次人口普查的100m分辨率人口栅格数据(免费获取\tif格式\2020年)

人口空间分布数据是我们在各项研究中经常使用的数据。之前我们分享过来源于LandScan数据集的2000-2022年的1km精度的人口空间分布栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01; 相较于LandScan全球人口数据集&#xff0c;我国历次人口普查的数据对于…

【node】初识node

前言 目标 1 为什么要学习node 2 node如何安装 #mermaid-svg-KR8iFyZTmb86RU67 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-KR8iFyZTmb86RU67 .error-icon{fill:#552222;}#mermaid-svg-KR8iFyZTmb86RU67 .error…

QT--QPushButton设置文本和图标、使能禁能、信号演示

按钮除了可以设置显示文本之外&#xff0c;还可以设置图标 文本 可以获取和设置按钮上显示的文本 // 获取和设置按钮的文本 QString text() const void setText(const QString &text)该属性&#xff0c;既可以在 Qt 设计师右侧的属性窗口中修改&#xff0c;也可以在代码…