文章目录

        • 前言
        • 1、HashMap类
          • 1.1、源码分析
          • 1.2、hashMap特点及使用建议
        • 2、LinkedHashMap类
          • 2.1、源码分析
        • 3、TreeMap类
          • 3.1 源码分析
        • 4、HashTable类
          • 4.1、源码分析
          • 4.2、HashMap与HashTable的比较

前言

下图为Map接口以及其相关子类实现类的简易结构图:
在这里插入图片描述
接下来对上图中的实现类的原理及使用方面进行一个简单的介绍。

1、HashMap类

    Hash的底层实现为数组+链表/红黑树的结构形式,在JDK1.8之前HashMap底层实现为数组+链表的形式,JDK1.8以后添加了红黑树,至于何时进行链表和红黑树的转换,待下面分析源码的时候进行讲解;先来展示下HashMap类的继承实现关系:

public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {.........}

下面是一个典型的HashMap的结构图:

图片来源于网络

1.1、源码分析

接下来看下实现类中的静态变量及成员变量:

    /*** 设置table初始化时的容量默认值为16*/static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16/*** 设置数组最大的容量值为2的30次方*/static final int MAXIMUM_CAPACITY = 1 << 30;/*** 默认负载因子为0.75*/static final float DEFAULT_LOAD_FACTOR = 0.75f;/*** 以下三个静态常量是链表和红黑树相互转换的阈值因子;* 链表和红黑树相互转换的条件:* 	1)链表中的元素个数大于8个,同时数组table的元素个数大于64时才会转换为红黑树* 	2)如果红黑树中的节点个数小于UNTREEIFY_THRESHOLD,则由红黑树转化为链表*/static final int TREEIFY_THRESHOLD = 8;static final int UNTREEIFY_THRESHOLD = 6;static final int MIN_TREEIFY_CAPACITY = 64;/********************成员变量**************************//*** table变量是一个Node数组,负责存储键值对*/transient Node<K,V>[] table;/*** 存放具体元素的集合*/transient Set<Map.Entry<K,V>> entrySet;/*** 记录map中键值对的个数*/transient int size;/*** 计数器,记录hashMap结构变化的次数*/transient int modCount;/*** 设置扩容的阈值,大小为数组table容量*负载因子* 例如:默认容量为16*默认负载因子0.75=12,当key-value的个数大于12时进行扩容操作* * 扩容后hashmap的容量为之前的2倍*/int threshold;/*** 真实装载因子,不设置默认为0.75;可以通过构造函数来修改*/final float loadFactor;

HashMap的构造函数:

/*** 指定初始化容量和负载因子的构造函数*/
public HashMap(int initialCapacity, float loadFactor) {// 判断初始化容量initialCapacity是否小于0if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);// 判断初始化容量initialCapacity是否大于集合的最大容量MAXIMUM_CAPACITY->2的30次幂                                   if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;// tableSizeFor(initialCapacity) 判断指定的初始化容量是否是2的n次幂,// 如果不是那么会变为比指定初始化容量大的最小的2的n次幂。this.threshold = tableSizeFor(initialCapacity);}// tableSizeFor() 实现,可以自己测试,返回的数据刚好如上边说的一样:如果不是2的n次幂那么会变为比指定初始化容量大的最小的2的n次幂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;}/*** 使用默认的负载因子,指定初始化容量*/public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}/*** 使用默认参数的无参构造函数*/public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; }/*** 根据输入Map构造一个新的HashMap对象*/public HashMap(Map<? extends K, ? extends V> m) {this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false);}final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {// 获取参数集合的长度int s = m.size();// 判断参数集合的长度是否大于0if (s > 0) {// 判断table数组是否初始化if (table == null) { // pre-sizefloat ft = ((float)s / loadFactor) + 1.0F;int t = ((ft < (float)MAXIMUM_CAPACITY) ?(int)ft : MAXIMUM_CAPACITY);// 如果t大于阈值,则初始化阈值if (t > threshold)threshold = tableSizeFor(t);}// 已经初始化且s大于阈值,则进行扩容操作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);}}}

注意:在上述中我们讲到tableSizeFor()会将设定非2的n次幂的容量值转化为大于该值的最小2的n次幂的值;threshold 代表的是数组的阈值,等于数组容量*负载因子;但是上面介绍的 threshold = tableSizeFor(容量),这是因为这里计算的threshold只是一个初始化的值,而在进行put的时候会对threshold重新计算。

添加数据的方法:

	//先来介绍下hashMap中对key进行hash的方法static final int hash(Object key) {int h;/*** 1)当key为null时,hash的值为0* 2)当不为null时,使用key的hashCode值h与h右移16位以后的值进行或非(^)的操作*/return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}// 注意:这个hash()计算出来的并不是元素在数组table中的位置,如果要计算在数组中的位置还要进行下一步的操作:// 使用hash()计算出来的值与数组table的容量值n减去1进行求与(&)的操作// int index=(n - 1) & hash;/***	Put方法的实现步骤大致如下:*	 1)先通过hash值计算出key映射到哪个bucket,即计算出table中的索引;*	 2)如果bucket上没有碰撞冲突,则直接插入;*	 3)如果出现碰撞冲突了,则需要处理冲突:*		a、如果该bucket使用红黑树处理冲突,则调用红黑树的方法插入数据;*		b、否则采用传统的链式方法插入。如果链的长度达到临界值,则把链转变为红黑树;*   4)如果bucket中存在重复的键,则为该键替换新值value;* 	 5)如果size大于阈值threshold,则进行扩容;*/public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;/*1)transient Node<K,V>[] table; 表示存储Map集合中元素的数组。2)(tab = table) == null 表示将空的table赋值给tab,然后判断tab是否等于null,第一次肯定是			null3)(n = tab.length) == 0 表示将数组的长度0赋值给n,然后判断n是否等于0,n等于0由于if判断使用双或,满足一个即可,则执行代码 n = (tab = resize()).length; 进行数组初始化。并将初始化好的数组长度赋值给n.4)执行完n = (tab = resize()).length,数组tab每个空间都是null*/if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;/*1)i = (n - 1) & hash 表示计算数组的索引赋值给i;2)p = tab[i = (n - 1) & hash]表示获取计算出的位置的数据赋值给节点p3) (p = tab[i = (n - 1) & hash]) == null 判断节点位置是否等于null,如果为null,则执行代码:tab[i] = newNode(hash, key, value, null);根据键值对创建新的节点放入该位置的bucket中小结:如果当前bucket没有哈希碰撞冲突,则直接把键值对插入空间位置*/ if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {// 当前位置已经有值时Node<K,V> e; K k;// 判断新插入节点的key是否与当前节点原有的数据的key相同if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))// 相同的话则将p赋给ee = p;// 判断当前节点是否为树节点else if (p instanceof TreeNode)// 如果为树节点则使用红黑树的添加方式e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {// 原来的结构为链表格式的结构// 采用循环遍历的方式,判断链表中是否有重复的key,如果有则覆盖value;如果没有则添加到链表的最后for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);// 判断链表中的节点个数是否大于TREEIFY_THRESHOLD临界值8,如果大于则将链表转换为红黑树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;}}// 如果e为null,说明原来的map中没有与插入的节点相重复的keyif (e != null) { // existing mapping for keyV oldValue = e.value;// onlyIfAbsent 如果true代表不更改现有的值// onlyIfAbsent=false说明原来的值要被覆盖if (!onlyIfAbsent || oldValue == null)e.value = value;// 执行回调afterNodeAccess(e);// 返回旧值return oldValue;}}// 记录修改的次数++modCount;// 修改size,如果map中键值对的个数size大于阈值则进行扩容if (++size > threshold)resize();afterNodeInsertion(evict);return null;}

将链表转化为红黑树的操作:

  /**替换指定哈希表的索引处桶中的所有链接节点Node<K,V>[] tab = tab 数组名int hash = hash表示哈希值*/final void treeifyBin(Node<K,V>[] tab, int hash) {int n, index; Node<K,V> e;/*如果当前数组为空或者数组的长度小于进行树形化的阈值(MIN_TREEIFY_CAPACITY = 64),就去扩容。而不是将节点变为红黑树。目的:如果数组很小,那么转换红黑树,然后遍历效率要低一些。这时进行扩容,那么重新计算哈希值,链表长度有可能就变短了,数据会放到数组中,这样相对来说效率高一些。*/if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)resize();else if ((e = tab[index = (n - 1) & hash]) != null) {/*1)执行到这里说明哈希表中的数组长度大于阈值64,开始进行树形化2)e = tab[index = (n - 1) & hash]表示将数组中的元素取出赋值给e,e是哈希表中指定位置桶里的链表节点,从第一个开始*///hd:红黑树的头结点   tl :红黑树的尾结点TreeNode<K,V> hd = null, tl = null;do {//新创建一个树的节点,内容和当前链表节点e一致TreeNode<K,V> p = replacementTreeNode(e, null);if (tl == null)//将新创键的p节点赋值给红黑树的头结点hd = p;else {/*p.prev = tl:将上一个节点p赋值给现在的p的前一个节点tl.next = p;将现在节点p作为树的尾结点的下一个节点*/p.prev = tl;tl.next = p;}tl = p;/*e = e.next 将当前节点的下一个节点赋值给e,如果下一个节点不等于null则回到上面继续取出链表中节点转换为红黑树*/} while ((e = e.next) != null);/*让桶中的第一个元素即数组中的元素指向新建的红黑树的节点,以后这个桶里的元素就是红黑树而不是链表数据结构了*/if ((tab[index] = hd) != null)hd.treeify(tab);}}

扩容原理以及对应的源码

    final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;// 获取当前数组的长度int oldCap = (oldTab == null) ? 0 : oldTab.length;//当前阀值点 默认是12(16*0.75)int oldThr = threshold;int newCap, newThr = 0;if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}/*** 1)通过位移运算,将容量扩大为原来的2倍,且扩大2倍以后仍然要小于最大容量* 2)原来的容量大小要大于等于默认的初始化容量16*/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 thresholdnewCap = oldThr;else {               // zero initial threshold signifies using defaults// 直接使用默认值newCap = 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;// 创建新的hash表@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 orderNode<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;}
1.2、hashMap特点及使用建议

特点:

  • 存取无序的
  • 键和值位置都可以是null,但是键位置只能是一个null,因为键不能重复
  • 键位置是唯一的,底层的数据结构控制键的
  • jdk1.8前数据结构是:链表 + 数组 jdk1.8之后是 : 链表 + 数组 /红黑树
  • 阈值(边界值) > 8 并且数组长度大于64,才将链表转换为红黑树,变为红黑树的目的是为了高效的查询。

使用时建议:

  • 使用时如果知道数据量的大小,尽量使用设置初始容量的构造函数
  • 建议设置初始容量的计算公式为:需要存储的元素个数/ 负载因子(0.75F) + 1.0F

2、LinkedHashMap类

    LinkedHashMap是HashMap的子类,对于数据进行存储的过程与HashMap中的一致;但是也有一定的区别,这里对两者的区别以及LinkedHashMap的特点进行简单总结:

  • HashMap是无序的,但是LinkedHashMap则是有序的,默认是按照数据插入的顺序;
  • jdk1.8以后,HashMap的数据结构为 链表 + 数组 /红黑树,LinkedHashmap数据结构为: 链表 + 双向数组 /红黑树;
  • 键和值位置都可以是null,但是键位置只能是一个null;
  • 两个实现类均为非线程安全。
2.1、源码分析

先来看一下LinkedHashMap的集成关系:

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{......}

LinkedHashMap中新增加的成员变量:

    /*** The head (eldest) of the doubly linked list.* 双向链表的头节点,最先插入的节点*/transient LinkedHashMap.Entry<K,V> head;/*** The tail (youngest) of the doubly linked list.* 双向链表的尾节点,最后插入的节点*/transient LinkedHashMap.Entry<K,V> tail;/*** 用来设置顺序,默认为false:按照插入的顺序来排序* 设置为true:按照访问的顺序来排序,最近访问的数据节点会被插入到链表的最末端*/final boolean accessOrder;

再来看一下LinkedHashMap的真实结构如下:

    static class Entry<K,V> extends HashMap.Node<K,V> {Entry<K,V> before, after;Entry(int hash, K key, V value, Node<K,V> next) {super(hash, key, value, next);}}

与HashMap相比,LinkedHashMap在原来的基础上添加了两个属性before,after;所以现在Entry(对应HashMap为Node)的属性包括以下几个:

K key
V value
Entry<K, V> next
int hash
// 下面为新增的
Entry<K, V> before
Entry<K, V> after

再来看一下LinkedHashMap的构造函数,LinkedHashMap共包括5个构造函数,如下所示:

	// 与HashMap的构造函数相比,只是在每一个构造函数中多了一个控制顺序的设置,// 默认是按照插入的顺序排序public LinkedHashMap(int initialCapacity, float loadFactor) {super(initialCapacity, loadFactor);accessOrder = false;}public LinkedHashMap(int initialCapacity) {super(initialCapacity);accessOrder = false;}public LinkedHashMap() {super();accessOrder = false;}public LinkedHashMap(Map<? extends K, ? extends V> m) {super();accessOrder = false;putMapEntries(m, false);}public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {super(initialCapacity, loadFactor);this.accessOrder = accessOrder;}

接下里说一下LinkedHashMap是如何控制顺序的,先来看一下LinkedHashMap获取节点数据的过程,代码如下:

    public V get(Object key) {Node<K,V> e;if ((e = getNode(hash(key), key)) == null)return null;// 如果设置了accessOrder=true,则重新进行序列调整;否则按照插入顺序if (accessOrder)afterNodeAccess(e);return e.value;}// 将已经获取的节点,重新覆写到链表的尾节点void afterNodeAccess(Node<K,V> e) { // move node to lastLinkedHashMap.Entry<K,V> last;// accessOrder=true// 且刚获取的节点不在链表的尾部,就将节点移动到链表的尾部if (accessOrder && (last = tail) != e) {LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;p.after = null;// 如果刚获取的节点的前一个节点为null,说明该节点在头部,设置其下一个节点为头节点// 否则就将当前节点的下一个节点设置为其前一个节点的下一个节点 if (b == null)head = a;elseb.after = a;if (a != null)a.before = b;elselast = b;if (last == null)head = p;else {p.before = last;last.after = p;}tail = p;// 计数修改的次数++modCount;}}

接下来介绍一下LinkedHashMap添加数据的过程,LinkedHashMap添加数据时调用的是父类的put方法,但在具体执行的时候则对其中的一些方法进行了重写,先来看一下父类的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) {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)// LinkedHashMap对newNode()进行了重写,下边会介绍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当accessOrder=true时,* 将操作(查询、覆盖值)过的节点移动到链尾,具体看上边的源码分析*/afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();// 这个方法,在jdk1.8中没有用到,具体参见下边的分析afterNodeInsertion(evict);return null;}

具体的过程就不解释了,在LinkedHashMap中,对上述方法中的newNode()、putTreeVal()中的newTreeNode()进行了重写,并且实现了两个回调方法afterNodeAccess(上面已经介绍过了)和afterNodeInsertion,下面看一下:

   Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {LinkedHashMap.Entry<K,V> p =new LinkedHashMap.Entry<K,V>(hash, key, value, e);linkNodeLast(p);return p;}// 新建一个节点,并将节点方至链表的尾部private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {LinkedHashMap.Entry<K,V> last = tail;tail = p;if (last == null)head = p;else {p.before = last;last.after = p;}}// afterNodeInsertion()没有用到,因为removeEldestEntry()方法始终返回falsevoid afterNodeInsertion(boolean evict) { // possibly remove eldestLinkedHashMap.Entry<K,V> first;if (evict && (first = head) != null && removeEldestEntry(first)) {K key = first.key;removeNode(hash(key), key, null, false, true);}}

最后来说一下移除节点的操作,移除的过程大致如下:

  • 先根据key计算节点在数组table中的索引位置
  • 遍历链表或者红黑树删除节点
  • 移除节点以后维护双向链表
    public V remove(Object key) {Node<K,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value;}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;// 与HashMap有区别的地方是,移除完节点以后对于双向链表结构的维护afterNodeRemoval(node);return node;}}return null;}void afterNodeRemoval(Node<K,V> e) { // unlinkLinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;// 将要移除的节点的前驱和后继节点置为nullp.before = p.after = null;// b为null,说明要删除的节点为头节点if (b == null)head = a;elseb.after = a;// a为null,说明要删除的节点为尾节点if (a == null)tail = b;elsea.before = b;}

关于LinkedHashMap暂时就讲到这里,关于未说到的可以自己看源码在研究,跟HashMap的内容差不多。
参考博客:
LinkedHashMap 源码详细分析(JDK1.8)
LinkedHashMap如何保证顺序性

3、TreeMap类

    TreeMap是一种可以实现自排序的数据结构,底层的数据结构为红黑树,红黑树是平衡树中的一种,关于红黑树的性质可以自己下去研究,这里就不展开了,接下来进行源码分析。

3.1 源码分析

先来看一下TreeMap的继承关系:

public class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, java.io.Serializable{......}

接下来介绍下实现类中的成员变量及构造函数:

    /*** The comparator used to maintain order in this tree map, or* null if it uses the natural ordering of its keys.* 自定义比较器,可以为null,如果为null时则默认使用key来进行排序*/private final Comparator<? super K> comparator;// 红黑树的根节点private transient Entry<K,V> root;/*** entry的个数*/private transient int size = 0;/*** The number of structural modifications to the tree.* 结构改变的次数*/private transient int modCount = 0;/*** 构建一个空的构造函数*/public TreeMap() {comparator = null;}/*** 根据自定义的比较器来构建构造函数*/public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;}/*** 使用默认比较器将Map类型数据转化为一个新的TreeMap结构*/public TreeMap(Map<? extends K, ? extends V> m) {comparator = null;putAll(m);}/*** Constructs a new tree map containing the same mappings and* using the same ordering as the specified sorted map.  This* method runs in linear time.** @param  m the sorted map whose mappings are to be placed in this map,*         and whose comparator is to be used to sort this map* @throws NullPointerException if the specified map is null* 使用已知的SortedMap对象来构建TreeMap*/public TreeMap(SortedMap<K, ? extends V> m) {comparator = m.comparator();try {buildFromSorted(m.size(), m.entrySet().iterator(), null, null);} catch (java.io.IOException cannotHappen) {} catch (ClassNotFoundException cannotHappen) {}}

再来看一下TreeMap每一个节点中的真实结构:

    static final class Entry<K,V> implements Map.Entry<K,V> {K key;V value;Entry<K,V> left;Entry<K,V> right;Entry<K,V> parent;boolean color = BLACK;}

一个典型的红黑树节点结构,其中color属性用来设置节点的链接是黑链接还是红链接;
添加数据的操作:

  public V put(K key, V value) {Entry<K,V> t = root;// 如果根节点为null,则创建根节点if (t == null) {/*** 检查key类型,这一点在构造函数的注释上就可以看到原因* All keys inserted into the map must be <em>mutually* comparable</em> by the given comparator: {@code comparator.compare(k1,* k2)} must not throw a {@code ClassCastException} for any keys* {@code k1} and {@code k2} in the map.*/compare(key, key); // type (and possibly null) checkroot = new Entry<>(key, value, null);size = 1;modCount++;return null;}int cmp;Entry<K,V> parent;// split comparator and comparable pathsComparator<? super K> cpr = comparator;if (cpr != null) {// 这里执行的是一个红黑树遍历比较的操作do {parent = t;cmp = cpr.compare(key, t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}else {// 用户没有设定比较器时,使用默认比较器对key进行遍历比较// TreeMap的key值不能为nullif (key == null)throw new NullPointerException();@SuppressWarnings("unchecked")Comparable<? super K> k = (Comparable<? super K>) key;do {parent = t;cmp = k.compareTo(t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}Entry<K,V> e = new Entry<>(key, value, parent);if (cmp < 0)parent.left = e;elseparent.right = e;// 执行完上边的操作以后,节点已经被插入到树中// fixAfterInsertion()方法是对插入的节点进行调整,就是红黑树中着色、左旋、右旋使树再次平衡,这里可以参考红黑树的调整过程fixAfterInsertion(e);size++;modCount++;return null;}

移除节点:

    public V remove(Object key) {Entry<K,V> p = getEntry(key);// 如果没有找到,返回nullif (p == null)return null;// 找到以后返回原来的数据V oldValue = p.value;// 删除指定的entrydeleteEntry(p);return oldValue;}// 先根据key找到指定的entryfinal Entry<K,V> getEntry(Object key) {// Offload comparator-based version for sake of performanceif (comparator != null)// 根据特定的比较器来找到entryreturn getEntryUsingComparator(key);if (key == null)throw new NullPointerException();@SuppressWarnings("unchecked")Comparable<? super K> k = (Comparable<? super K>) key;Entry<K,V> p = root;while (p != null) {int cmp = k.compareTo(p.key);if (cmp < 0)p = p.left;else if (cmp > 0)p = p.right;elsereturn p;}return null;}

关于TreeMap这里讲解的比较简单,关于红黑树的具体过程没有详细展开来说,有兴趣的可以自己下去研究,或者参考此篇博客中的内容:【深入理解java集合】-TreeMap实现原理

小结:

  • TreeMap使用时可以自己定义排序比较的规则,可扩展性更好
  • TreeMap的键key不能为null,值可以为null
  • TreeMap有序但是非线程安全

4、HashTable类

    在JDK1.8中,HashTable采用数组+链表的数据结构,大致的结构如下:

在这里插入图片描述
图片来源于网络

4.1、源码分析

首先我们来看一下HashTable的继承关系:

public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, java.io.Serializable {......}

在HashTable中包含有5个成员变量,4个构造函数,接下来我们对这部分内容进行介绍:

    /*** 存储数据的Entry数组,与hashMap中的一致*/private transient Entry<?,?>[] table;/*** 记录entry的个数,即键值对的个数*/private transient int count;/*** The table is rehashed when its size exceeds this threshold.  (The* value of this field is (int)(capacity * loadFactor).)* 扩容的阈值等于table的容量*负载因子*/private int threshold;/*** 负载因子,默认为0.75*/private float loadFactor;/*** 计数器,记录hashTable结构变化的次数*/private transient int modCount = 0;/***********************HashTable构造函数*******************************//*** 构造函数的具体实现:* 	 1)如果设置的容量为0,则默认设置为1* 	 2)初始化数组table*   3) 扩容的阈值为数组的容量(默认为11)*负载因子(默认为0.75)* Hashtable中数组的容量没有限制为2的n次幂,可以取大于0的任意值*/public Hashtable(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal Load: "+loadFactor);if (initialCapacity==0)initialCapacity = 1;this.loadFactor = loadFactor;table = new Entry<?,?>[initialCapacity];threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);}/*** Constructs a new, empty hashtable with the specified initial capacity* and default load factor (0.75).*/public Hashtable(int initialCapacity) {this(initialCapacity, 0.75f);}/*** Constructs a new, empty hashtable with a default initial capacity (11)* and load factor (0.75).*/public Hashtable() {this(11, 0.75f);}/*** Constructs a new hashtable with the same mappings as the given* Map.  The hashtable is created with an initial capacity sufficient to* hold the mappings in the given Map and a default load factor (0.75).** @param t the map whose mappings are to be placed in this map.* @throws NullPointerException if the specified map is null.* @since   1.2*/public Hashtable(Map<? extends K, ? extends V> t) {this(Math.max(2*t.size(), 11), 0.75f);putAll(t);}	

接下来看一下HashTable添加数据的过程:

	/*** 添加的方法采用synchronized 关键字进行修饰,所以是线程安全的*/public synchronized V put(K key, V value) {// value不能为空,否则抛出空指针异常;if (value == null) {throw new NullPointerException();}// Makes sure the key is not already in the hashtable.Entry<?,?> tab[] = table;// key也不能为null,因为null.hashCode会抛出异常int hash = key.hashCode();// 计算entry在数组table中的索引的计算方法,hash值与2^31-1求与,然后与数组的长度进行求余int index = (hash & 0x7FFFFFFF) % tab.length;@SuppressWarnings("unchecked")Entry<K,V> entry = (Entry<K,V>)tab[index];// 遍历table[index]所连接的所有链表,查找是否有节点的key与要插入的key相同,如果存在则替换for(; entry != null ; entry = entry.next) {if ((entry.hash == hash) && entry.key.equals(key)) {V old = entry.value;entry.value = value;return old;}}// 如果不存在相同的key,则执行addEntry操作addEntry(hash, key, value, index);return null;}private void addEntry(int hash, K key, V value, int index) {modCount++;Entry<?,?> tab[] = table;// 判断hashTable中的键值对的个数是否大于等于扩容阈值,// 如果大于等于,则执行扩容的操作if (count >= threshold) {// Rehash the table if the threshold is exceededrehash();tab = table;hash = key.hashCode();index = (hash & 0x7FFFFFFF) % tab.length;}// Creates the new entry.@SuppressWarnings("unchecked")// 取出table中index位置的entry给到e,也就是对应位置链表给到eEntry<K,V> e = (Entry<K,V>) tab[index];// 把新插入的entry插入到链表的第一个节点tab[index] = new Entry<>(hash, key, value, e);count++;}

扩容的操作:

    @SuppressWarnings("unchecked")protected void rehash() {int oldCapacity = table.length;Entry<?,?>[] oldMap = table;// overflow-conscious code// 新的数组的容量为原来数组的2倍+1int newCapacity = (oldCapacity << 1) + 1;// 判断新的数组容量值是否大于最大值if (newCapacity - MAX_ARRAY_SIZE > 0) {if (oldCapacity == MAX_ARRAY_SIZE)// Keep running with MAX_ARRAY_SIZE bucketsreturn;newCapacity = MAX_ARRAY_SIZE;}//使用新的数组容量初始化一个新的数组Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];modCount++;// 计算扩容阈值threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);table = newMap;// 循环遍历将原来的数据添加到新的数组中for (int i = oldCapacity ; i-- > 0 ;) {for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {Entry<K,V> e = old;old = old.next;int index = (e.hash & 0x7FFFFFFF) % newCapacity;e.next = (Entry<K,V>)newMap[index];newMap[index] = e;}}}

最后看一下移除数据的操作:

    public synchronized V remove(Object key) {Entry<?,?> tab[] = table;int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;@SuppressWarnings("unchecked")// 先根据key找到table中对应的索引位置处Entry<K,V> e = (Entry<K,V>)tab[index];// 循环遍历数组index索引处对应的链表for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {modCount++;// prev==null,说明链表中的第一个节点即为要移除的节点// 执行的是一个删除链表中节点的操作,相对简单if (prev != null) {prev.next = e.next;} else {tab[index] = e.next;}count--;V oldValue = e.value;e.value = null;return oldValue;}}return null;}
4.2、HashMap与HashTable的比较
  • HashMap中的键和值均可以为null,但是HashTable中的键和值均不能为null
  • JDK1.8中HashMap采用数组+链表/红黑树结构实现,而HashTable中采用的是数组+链表实现
  • HashMap中出现hash冲突时,如果链表节点数小于8时是将新元素加入到链表的末尾(大于8时就要扩容操作了),而HashTable中出现hash冲突时采用的是将新元素加入到链表的开头
  • HashMap中数组容量的大小要求是2的n次方,如果初始化时不符合要求会进行调整,而HashTable中数组容量的大小可以为任意正整数
  • 计算entry在数组中的位置的策略不同
  • HashMap不是线程安全的,HashTable为线程安全的,其中的添加、移除、获取数据的方法均添加了synchronized关键字
  • HashMap的默认容量为16,HashTable默认容量为11
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Nginx屏蔽或禁止指定来源网站访问

    在nginx.conf的server配置项中加入 1.访问返回403 if ($http_referer ~* "xxx.com") { return 403; }2.访问跳转指定网站 if ($http_referer ~* "xxx.com") { rewrite ^/ http://www.xxx.com/;}3.禁止多域名 if ($http_referer ~* "xxx.com|aaa.…...

    2024/4/20 23:22:41
  2. 联诚支付,能更使用具有信任证明的区块链技术进行跨境支付

    当前系统 在单个国家/地区的银行之间结算付款可能是一个复杂且耗时的过程。在解决银行间交易或购买商品时&#xff0c;中央银行和商业银行都起着重要作用1。商业银行需要通过在账户和其他银行之间转移余额来解决个人或企业之间的经济行为。中央银行在这一过程中起着重要作用。…...

    2024/4/28 5:05:30
  3. javaWeb项目哪些地方有用多线程

    问&#xff1a;能不能简单描述一下你在java web开发中需要用到多线程编程的场景&#xff1f; 回答一&#xff1a; 最典型的如&#xff1a; 1、用户注册完成送大礼包/积分之类&#xff0c;且积分等也是另一个系统并比较耗时&#xff1b;且这类任务即使失败也不是特别重要的。 …...

    2024/4/27 7:15:29
  4. 学习python,怎能不懂点PEP呢?

    作者&#xff1a;豌豆花下猫 来源&#xff1a;Python猫 或许你是一个初入门Python的小白&#xff0c;完全不知道PEP是什么。又或许你是个学会了Python的熟手&#xff0c;见过几个PEP&#xff0c;却不知道这玩意背后是什么。那正好&#xff0c;本文将系统性地介绍一下PEP&…...

    2024/4/21 4:52:29
  5. 2020数学建模E题思路

    为了估计不同大雾情况下对应的能见度以及预测大雾的消散&#xff0c;请回答以下问题&#xff1a; 一&#xff0e;众所周知&#xff0c;雾与近地面的气象因素有关。建立模型描述能见度与地面气象观测&#xff08;温度、湿度和风速等&#xff09;之间的关系&#xff0c;并针对题目…...

    2024/4/7 10:42:13
  6. vector用法

    在不知道数组长度的情况下遍历数组可以用容器:vector; 具体用法: 1:所在文件 #include; 一、vector 的初始化&#xff1a;可以有五种方式,举例说明如下&#xff1a; (1) vector<int> a(10); //定义了10个整型元素的向量&#xff08;尖括号中为元素类型名&#xff0c;它…...

    2024/4/7 10:42:13
  7. Cocos2d-x网络模块3:Socket连接(1)

    通常来说&#xff0c;在客户端游戏开发中&#xff0c;使用HTTP进行网络通信的比较少见&#xff0c;一般使用的都是Socket进行通信。而HTTP则主要用于网页或者网页游戏。 使用第三方Socket通信库&#xff1a;ODSocket。 Socket简介 1、套接字(socket)概念 套接字(socket)是通…...

    2024/4/21 0:57:24
  8. 小程序 scroll-view 弹窗 穿透问题

    在弹窗标签上添加属性&#xff1a;catchtouchmove"true"&#xff0c;即可 <view class"tc_con" wx:if"{{warningShow}}" catchtouchmove"true"><view class"con_ma"><view class"con_title">&…...

    2024/4/23 4:09:49
  9. K8S基础系列1----基本组件介绍

    一、master节点和node的区别 K8S集群的master节点和node节点&#xff0c;区别就是运行的服务不一样 master节点运行的服务&#xff1a;Etcd、ApiServer、ControllerManager、Scheduler node节点运行的服务&#xff1a;Kubelet、Kube-Proxy二、口语化介绍各组件的功能&#xff…...

    2024/4/18 13:00:57
  10. 因琐事怒删男友论文,1个月心血全没,防删4步走!

    “因琐事争端把男友论文及资料全删”这个话题冲上了各大平台的热搜榜&#xff0c;光微博就贡献了2亿个阅读&#xff0c;知乎各大衍生话题区热闹非凡&#xff0c;云宝贝这就来给大家说说这件事的来龙去脉。 起因是一个女生因为心疼医学博士生男友工作辛苦&#xff0c;还要没日…...

    2024/4/28 20:21:31
  11. 微惯性导航系统简介

    惯性导航系统最先应用于火箭制导&#xff0c;美国火箭先驱罗伯特戈达德(ROBERT GODDARD)试验了早期的陀螺系统。二战期间经德国人冯布劳恩改进应后&#xff0c;应用于V-2火箭制导。战后美国麻省理工学院等研究机构及人员对惯性制导进行深入研究&#xff0c;从而发展成应用于飞机…...

    2024/4/7 11:11:46
  12. 界面控件DevExpress 2020年最强资源合集,让界面开发变得更轻松!

    热门控件推荐 屡获大奖的软件开发平台DevExpress 2020年全新首发v20.1&#xff0c;最新版拥有众多新产品和数十个具有高影响力的功能&#xff0c;可为桌面、Web和移动应用提供直观的解决方案&#xff0c;全面解决各种使用场景问题。下面为大家推荐几款最热门的控件&#xff1a…...

    2024/4/7 11:11:45
  13. 制作横版游戏KillBear第9课:暂停层+屏蔽下层监听

    在上一节课程里&#xff0c;我们实现了添加一个有冷却效果的技能按钮&#xff0c;教你的英雄增加炫目的大招&#xff0c;如果你还打算有其他技能的话&#xff0c;依次添加就可以了&#xff0c;就不再多说。本课讲述在游戏层中右上角添加一个按钮,用于暂停游戏功能的实现。 开发…...

    2024/4/7 11:11:44
  14. Flutter - 仿微信朋友圈效果实现

    ### demo 地址: https://github.com/iotjin/jh_flutter_demo ####实现效果&#xff1a; appbar滚动颜色渐变&#xff0c;状态栏颜色切换背景图下拉放大图片9宫格展示图片全屏浏览&#xff0c;长按弹框 效果图&#xff1a; 代码 import dart:convert; import package:flutter/…...

    2024/4/22 20:58:22
  15. 数字货币交易所系统开发方案,交易所搭建源码

    合约跟单平台开发app&#xff0c;智能跟单软件开发技术 合约跟单平台开发指的什么意思&#xff1f; 所谓跟单&#xff0c;也就是跟随别人的下单去重复下单&#xff0c;当然这里的“别人”不是指普通的交易者&#xff0c;而是指一些具有交易经验、金融领域从业多年、行业大佬等…...

    2024/4/7 11:11:42
  16. java调用JenkinsServer API操作

    java调用JenkinsServer API操作1.下载配置jenkins1.1 下载jenkins1.2 配置jenkins2.java调用JenkinsServerServer API2.1、Jenkins API 格式2.2 操作Jenkins 视图 &#xff08;view&#xff09;2.3 操作 Jenkins 任务&#xff08;job&#xff09;2.4 操作 Jenkins 编译&#xf…...

    2024/4/25 6:20:45
  17. AJAX声明/监听/打开/发送

    <script>function loadXMLDoc() {var xmlhttp;if (window.XMLHttpRequest) {// IE7, Firefox, Chrome, Opera, Safari 浏览器执行代码xmlhttp new XMLHttpRequest();} else {// IE6, IE5 浏览器执行代码xmlhttp new ActiveXObject("Microsoft.XMLHTTP");}xm…...

    2024/4/7 11:11:40
  18. day08_方法入门

    类的成员之一&#xff1a;方 法(method) 方法是类或对象行为特征的抽象&#xff0c;用来完成某个功能操作。在某些语言中 也称为函数或过程。将功能封装为方法的目的是&#xff0c;可以实现代码重用&#xff0c;简化代码Java里的方法不能独立存在&#xff0c;所有的方法必须定…...

    2024/4/7 11:11:39
  19. FlutterListView组件属性详解

    FlutterListView相关属性 ListView.builder({//listView 常用 属性Key key,Axis scrollDirection Axis.vertical,//滚动方向bool reverse false,ScrollController controller, // 滚动控制 主要是控制滚动位置和监听滚动事件//primary 为true时 controller 必须为null&#…...

    2024/4/21 9:43:51
  20. 游戏建模师很抢手吗?

    《哪吒之魔童降世》票房突破40亿。导演饺子也因为这部电影走红&#xff0c;更多人开始了解台前幕后的故事&#xff0c;而在这之中“建模师”三个字频繁出现在大众眼前。 1、建模师是做什么的&#xff1f; 2、建模师有什么发展前途&#xff1f; 3、成为一个优秀的建模师有什么…...

    2024/4/7 11:11:37

最新文章

  1. 数列与级数(中)

    数列与级数 级数 如果没有相反的声明&#xff0c;所考虑的一切序列和级数都是复数值的 定义 3.21 设有序列 { a n } \left\{ a_{n} \right\} {an​}&#xff0c;我们用 ∑ n p q a n ( p ≤ q ) \sum_{np}^{q}a_{n} \left( p \le q \right) np∑q​an​(p≤q) 表示和 a p …...

    2024/5/4 9:18:55
  2. 梯度消失和梯度爆炸的一些处理方法

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

    2024/3/20 10:50:27
  3. 机器学习——模型融合:平均法

    机器学习——模型融合&#xff1a;平均法 在机器学习领域&#xff0c;模型融合是一种通过结合多个基本模型的预测结果来提高整体模型性能的技术。模型融合技术通常能够降低预测的方差&#xff0c;提高模型的鲁棒性&#xff0c;并在一定程度上提高预测的准确性。本文将重点介绍…...

    2024/5/4 3:01:17
  4. 【项目新功能开发篇】开发编码

    作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…...

    2024/5/3 2:06:18
  5. 阿里云弹性计算通用算力型u1实例性能评测,性价比高

    阿里云服务器u1是通用算力型云服务器&#xff0c;CPU采用2.5 GHz主频的Intel(R) Xeon(R) Platinum处理器&#xff0c;ECS通用算力型u1云服务器不适用于游戏和高频交易等需要极致性能的应用场景及对业务性能一致性有强诉求的应用场景(比如业务HA场景主备机需要性能一致)&#xf…...

    2024/5/1 13:48:44
  6. 416. 分割等和子集问题(动态规划)

    题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义&#xff1a;dp[i][j]表示当背包容量为j&#xff0c;用前i个物品是否正好可以将背包填满&#xff…...

    2024/5/3 11:50:27
  7. 【Java】ExcelWriter自适应宽度工具类(支持中文)

    工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...

    2024/5/2 16:04:58
  8. Spring cloud负载均衡@LoadBalanced LoadBalancerClient

    LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon&#xff0c;直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件&#xff0c;我们讨论Spring负载均衡以Spring Cloud2020之后版本为主&#xff0c;学习Spring Cloud LoadBalance&#xff0c;暂不讨论Ribbon…...

    2024/5/2 23:55:17
  9. TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案

    一、背景需求分析 在工业产业园、化工园或生产制造园区中&#xff0c;周界防范意义重大&#xff0c;对园区的安全起到重要的作用。常规的安防方式是采用人员巡查&#xff0c;人力投入成本大而且效率低。周界一旦被破坏或入侵&#xff0c;会影响园区人员和资产安全&#xff0c;…...

    2024/5/3 16:00:51
  10. VB.net WebBrowser网页元素抓取分析方法

    在用WebBrowser编程实现网页操作自动化时&#xff0c;常要分析网页Html&#xff0c;例如网页在加载数据时&#xff0c;常会显示“系统处理中&#xff0c;请稍候..”&#xff0c;我们需要在数据加载完成后才能继续下一步操作&#xff0c;如何抓取这个信息的网页html元素变化&…...

    2024/5/3 11:10:49
  11. 【Objective-C】Objective-C汇总

    方法定义 参考&#xff1a;https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...

    2024/5/3 21:22:01
  12. 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】

    &#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格…...

    2024/5/3 23:17:01
  13. 【ES6.0】- 扩展运算符(...)

    【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数&#xff0…...

    2024/5/2 23:47:43
  14. 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?

    文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕&#xff0c;各大品牌纷纷晒出优异的成绩单&#xff0c;摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称&#xff0c;在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁&#xff0c;多个平台数据都表现出极度异常…...

    2024/5/3 13:26:06
  15. Go语言常用命令详解(二)

    文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令&#xff0c;这些命令可以帮助您在Go开发中进行编译、测试、运行和…...

    2024/5/3 1:55:15
  16. 用欧拉路径判断图同构推出reverse合法性:1116T4

    http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b&#xff0c;我们在 a i a_i ai​ 和 a i 1 a_{i1} ai1​ 之间连边&#xff0c; b b b 同理&#xff0c;则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然&#xff0…...

    2024/5/4 2:14:16
  17. 【NGINX--1】基础知识

    1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息&#xff0c;并安装一些有助于配置官方 NGINX 软件包仓库的软件包&#xff1a; apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...

    2024/5/3 16:23:03
  18. Hive默认分割符、存储格式与数据压缩

    目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限&#xff08;ROW FORMAT&#xff09;配置标准HQL为&#xff1a; ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...

    2024/5/3 1:55:09
  19. 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

    文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中&#xff0c;传感器和控制器产生大量周…...

    2024/5/2 8:37:00
  20. --max-old-space-size=8192报错

    vue项目运行时&#xff0c;如果经常运行慢&#xff0c;崩溃停止服务&#xff0c;报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中&#xff0c;通过JavaScript使用内存时只能使用部分内存&#xff08;64位系统&…...

    2024/5/3 14:57:24
  21. 基于深度学习的恶意软件检测

    恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞&#xff0c;例如可以被劫持的合法软件&#xff08;例如浏览器或 Web 应用程序插件&#xff09;中的错误。 恶意软件渗透可能会造成灾难性的后果&#xff0c;包括数据被盗、勒索或网…...

    2024/5/2 9:47:25
  22. JS原型对象prototype

    让我简单的为大家介绍一下原型对象prototype吧&#xff01; 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定&#xff0c;每一个构造函数都有一个 prototype 属性&#xff0c;指向另一个对象&#xff0c;所以我们也称为原型对象…...

    2024/5/4 2:00:16
  23. C++中只能有一个实例的单例类

    C中只能有一个实例的单例类 前面讨论的 President 类很不错&#xff0c;但存在一个缺陷&#xff1a;无法禁止通过实例化多个对象来创建多名总统&#xff1a; President One, Two, Three; 由于复制构造函数是私有的&#xff0c;其中每个对象都是不可复制的&#xff0c;但您的目…...

    2024/5/3 22:03:11
  24. python django 小程序图书借阅源码

    开发工具&#xff1a; PyCharm&#xff0c;mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 小程序 功能介绍&#xff1a; 用户端&#xff1a; 登录注册&#xff08;含授权登录&#xff09; 首页显示搜索图书&#xff0c;轮播图&#xff0…...

    2024/5/4 9:07:39
  25. 电子学会C/C++编程等级考试2022年03月(一级)真题解析

    C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...

    2024/5/3 1:54:59
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

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

    2022/11/19 21:17:18
  27. 错误使用 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
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

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

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

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

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

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

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

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

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

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

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

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

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,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
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2022/11/19 21:16:58
  45. 如何在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