一篇文章了解和使用Map和Set(HashMap/TreeMap/HashSet/TreeSet)

news2024/12/23 23:17:43

[本节目标]

*掌握HashMap/TreeMap/HashSet/TreeSet的使用

*掌握了解HashSet和HashSet背后的哈希原理和简单的实现

1. 搜索树

1.1 概念

二叉搜索树又称二叉排序树,它或者是一颗空树,或者是具有以下性质的二叉树:

1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
2.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
3.它的左右子树也分别为二叉搜索树

1.2 操作-查找 

代码思路:

先判断根节点,然后再分别判断左右子树.

public class Demo50 {
    class Treenode{
        int val;
        Treenode right;
        Treenode left;
        public Treenode(int val){
            this.val = val;
        }
    }
    public Treenode root;
    public boolean search(int key){
Treenode cur = root;
while(cur != null){
    if(cur.val == key){
        return true;
    } else if (cur.val < key) {
        cur = cur.left;
    }
    else{
        cur = cur.right;
    }
}
return false;
    }


}

1.3 操作-插入

代码思路: 

先用while循环找到插入的位置,然后再用parent记录下来,再创建一个新的,接在记录parent的子类.


public class Demo51 {
    class Treenode{
        int val;
      Treenode right;
       Treenode left;
        public Treenode(int val){
            this.val = val;
        }
    }
    public Treenode root;
    public boolean insert(int val){
if(root==null){
    Treenode root = new Treenode(val);
}
Treenode cur = root;
Treenode parent = null;
while(cur != null){
    if(cur.val < val){
        parent = cur;
        cur=cur.right;
    } else if (cur.val > val) {
        parent = cur;
        cur = cur.left;
    }
    else{
        return false;
    }
}
        Treenode node = new Treenode(val);
if(parent.val>val){
  parent.right = node;
}
else {
   parent.left = node;
}
return true;
    }
}

1.4 操作-删除(难点)

设待删除结点为 cur, 待删除结点的双亲结点为 parent

1. cur.left == null
1). cur 是 root,则 root = cur.right
2). cur 不是 root,cur 是 parent.left,则 parent.left = cur.right
3). cur 不是 root,cur 是 parent.right,则 parent.right = cur.right


2. cur.right == null
1). cur 是 root,则 root = cur.left
2). cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
3). cur 不是 root,cur 是 parent.right,则 parent.right = cur.left


3. cur.left != null && cur.right != null
1). 需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题

public class Demo52 {
    class Treenode{
        int val;
        Treenode right;
        Treenode left;
    }
    public Treenode root = null;
    public void remove(int key) {
        Treenode partent = null;
        Treenode cur = root;
        while (cur != null) {
            if (cur.val < key) {
                partent = cur;
                break;
            } else if (cur.val > key) {
                partent = cur;
                cur = cur.right;
            } else {
                removenode(cur, partent);
            }
        }
    }

        public void removenode(Treenode cur,Treenode partent) {
            if (cur.left == null) {
                if (root == cur) {
                    root = cur.right;
                } else if (cur == partent.left) {
                    partent.left = cur.right;
                } else {
                    partent.right = cur.right;
                }

            } else if (cur.right == null) {
                if(root == cur){
                    root = cur.left;
                }else if(cur == partent.left){
                    partent.left = cur.left;
                }
                else{
                    partent.right = cur.left;
                }
            } else {//图片的解说对应这里
Treenode targetParent = cur;
Treenode target = cur.right;
while(target.left != null){
    targetParent = target;
    target = target.left;
}
cur.val = target.val;
if(targetParent.left == target){
    targetParent.left = target.right;
}
else{
    targetParent.right =target.right;
}
            }
        }
}




1.6  性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

1.7 和集合类的关系

TreeMap 和 TreeSet 即 java 中利用搜索树实现的 Map 和 Set;实际上用的是红黑树,而红黑树是一棵近似平衡的二叉搜索树,即在二叉搜索树的基础之上 + 颜色以及红黑树性质验证,关于红黑树的内容后序再进行讲解。

2. 搜索

2.1 概念即场景

Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。以前常见的搜索方式有:

1. 直接遍历,时间复杂度为O(N),元素如果比较多效率会非常慢
2. 二分查找,时间复杂度为 ,但搜索前必须要求序列是有序的


上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:
1. 根据姓名查询考试成绩
2. 通讯录,即根据姓名查询联系方式
3. 不重复集合,即需要先搜索关键字是否已经在集合中
可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,本节介绍的Map和Set是一种适合动态查找的集合容器。

2.2  模型

一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以模型会有两种:

 纯 key 模型,比如:
有一个英文词典,快速查找一个单词是否在词典中
快速查找某个名字在不在通讯录中
2. Key-Value 模型,比如:
统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出现的次数>
梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号

而Map中存储的就是key-value的键值对,Set中只存储了Key。

3.  Map的使用

3.1 关于Map的说明

Map是一个接口类,该类没有继承自Collection,该类中存储的是<K,V>结构的键值对,并且K一定是唯一的,不能重复。

3.2 关于Map.Entry<K,V>的说明

Map.Entry<K, V> 是Map内部实现的用来存放<key, value>键值对映射关系的内部类,该内部类中主要提供了<key, value>的获取,value的设置以及Key的比较方式。

解释+例子:map.put("hello",5),就是对应Map.Entry<K, V>,entry是Map里面的一个接口,然后把K,V看成了一个整体,然后Set包含了很多个map.

注意:Map.Entry<K,V>并没有提供设置Key的方法

3.3 Map的常用方法

V get(Object key)                                                          返回 key 对应的 value
V getOrDefault(Object key, V defaultValue)                  返回 key 对应的 value,key 不存在,返回默认值
V put(K key, V value)                                                     设置 key 对应的 value
V remove(Object key)                                                    删除 key 对应的映射关系
Set<K> keySet()                                                            返回所有 key 的不重复集合
Collection<V> values()                                                  返回所有 value 的可重复集合
Set<Map.Entry<K, V>> entrySet()                                返回所有的 key-value 映射关系
boolean containsKey(Object key)                                 判断是否包含 key
boolean containsValue(Object value)                           判断是否包含 value

注意:
1. Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap
2. Map中存放键值对的Key是唯一的,value是可以重复的
3. 在TreeMap中插入键值对时,key不能为空,否则就会抛NullPointerException异常,value可以为空。但是HashMap的key和value都可以为空。
4. Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)。
5. Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。
6. Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入。
7. TreeMap和HashMap的区别(最后会讲到)

Map示例:

import java.util.Arrays;
import java.util.TreeMap;
import java.util.Map;

public class Mapdemo1 {
    public static void main(String[] args) {
        Map<String, String> treeMap = new TreeMap<>();
        treeMap.put("林冲", "豹子头");
        treeMap.put("鲁智深", "花和尚");
        treeMap.put("武松", "行者");
        treeMap.put("宋江", "及时雨");
        String str = treeMap.put("李逵", "黑旋风");
        System.out.println(treeMap.size());
        System.out.println(treeMap);
        System.out.println(str);

        // put(key,value): 注意key不能为空,但是value可以为空
        // key如果为空,会抛出空指针异常
        //treeMap.put(null, "花名");     这里会抛出异常
        treeMap.put("无名", null);
        System.out.println(treeMap.size());

        // put(key, value):
        // 如果key存在,会使用value替换原来key所对应的value,返回旧value
        treeMap.put("李逵", "铁牛");
        System.out.println(treeMap);

        // get(key): 返回key所对应的value
        // 如果key存在,返回key所对应的value
        // 如果key不存在,返回null
        System.out.println(treeMap.get("鲁智深"));
        System.out.println(treeMap.get("史进"));
        System.out.println("***************************");

        //GetOrDefault(): 如果key存在,返回与key所对应的value,如果key不存在,返回一个默认值
        System.out.println(treeMap.getOrDefault("李逵", "铁牛"));
        System.out.println(treeMap.getOrDefault("史进", "九纹龙"));

        //containKey(key):检测key是否包含在Map中,时间复杂度:O(logN)
        // 按照红黑树的性质来进行查找
        // 找到返回true,否则返回false
        System.out.println(treeMap.containsKey("林冲"));
        System.out.println(treeMap.containsKey("史进"));

        // 打印所有的key
        // keySet是将map中的key防止在Set中返回的
        for(String s : treeMap.keySet()){
            System.out.print(s + " ");
          }
        System.out.println();
          System.out.println("********************************");
          // 打印所有的value
          // values()是将map中的value放在collect的一个集合中返回的
          for(String s : treeMap.values()){
           System.out.print(s + " ");
           }
              System.out.println();
          // 打印所有的键值对
          // entrySet(): 将Map中的键值对放在Set中返回了
          for(Map.Entry<String, String> entry : treeMap.entrySet()){
            System.out.println(entry.getKey() + "--->" + entry.getValue());
              }
          System.out.println();
      
}

    }

4. Set的使用

Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key。

4.1 Set常见的方法和使用

方法                                                                                   解释
boolean add(E e)                                            添加元素,但重复元素不会被添加成功
void clear()                                                      清空集合
boolean contains(Object o)                             判断 o 是否在集合中
Iterator<E> iterator()                                       返回迭代器
boolean remove(Object o)                              删除集合中的 o
int size()                                                          返回set中元素的个数
boolean isEmpty()                                          检测set是否为空,空返回true,否则返回false
Object[] toArray()                                            将set中的元素转换为数组返回
boolean containsAll(Collection<?> c)             集合c中的元素是否在set中全部存在,是返回true,否则返回false
boolean addAll(Collection<? extends E> c)    将集合c中的元素添加到set中,可以达到去重的效果

注意:
1. Set是继承自Collection的一个接口类
2. Set中只存储了key,并且要求key一定要唯一
3. TreeSet的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的
4. Set最大的功能就是对集合中的元素进行去重
5. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序。
6. Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入
7. TreeSet中不能插入null的key,HashSet可以。
8. TreeSet和HashSet的区别【HashSet在最后会讲到】

Set示例

import java.util.TreeSet;
import java.util.Iterator;
import java.util.Set;
public class SetDemo1 {
    public static void main(String[] args) {

        Set<String> s = new TreeSet<>();
// add(key): 如果key不存在,则插入,返回ture
// 如果key存在,返回false
        boolean isIn = s.add("apple");
        s.add("orange");
        s.add("peach");
        s.add("banana");
        System.out.println(s.size());
        System.out.println(s);
        isIn = s.add("apple");
// add(key): key如果是空,抛出空指针异常
//s.add(null);
// contains(key): 如果key存在,返回true,否则返回false
        System.out.println(s.contains("apple"));
        System.out.println(s.contains("watermelen"));
// remove(key): key存在,删除成功返回true
// key不存在,删除失败返回false
// key为空,抛出空指针异常
        s.remove("apple");
        System.out.println(s);
        System.out.println("********");
        System.out.println(s.remove("watermelen"));
        System.out.println(s);
        Iterator<String> it = s.iterator();
        while (it.hasNext()) {
            System.out.print(it.next() + " ");
        }
        System.out.println();
    }
}


5. 哈希表

5.1 概念

顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O( ),搜索的效率取决于搜索过程中元素的比较次数。

理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

当向该结构中:
插入元素:
据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放

搜索元素:

对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功.

该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(HashTable)(或者称散列表)

例如:数据集合{1,7,6,4,5,9};
哈希函数设置为:hash(key) = key % capacity; capacity为存储元素底层空间总的大小。

用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快 问题:按照上述哈希方式,向集合中插入元素44,就会出现问题.

接下来我们就来讨论这些问题.

5.2 冲突-概念

对于两个数据元素的关键字 和 (i != j),有 != ,但有:Hash( ) == Hash( ),即:不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。

把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。

5.3 冲突-避免

首先,我们需要明确一点,由于我们哈希表底层数组的容量往往是小于实际要存储的关键字的数量的,这就导致一个问题,冲突的发生是必然的,但我们能做的应该是尽量的降低冲突率.

5.4 冲突-避免-哈希函数升级

引起哈希冲突的一个原因可能是:哈希函数设计不够合理。 哈希函数设计原则:

*哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间

*哈希函数计算出来的地址能均匀分布在整个空间中

*哈希函数应该比较简单
常见哈希函数

1.直接定制法(常用)

取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B 优点:简单、均匀 缺点:需要事先知道关键字的分布情况 使用场景:适合查找比较小且连续的情况 

2.除留余数法(常用)

设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址

3. 平方取中法--(了解)
假设关键字为1234,对它平方就是1522756,抽取中间的3位227作为哈希地址; 再比如关键字为4321,对它平方就是18671041,抽取中间的3位671(或710)作为哈希地址 平方取中法比较适合:不知道关键字的分布,而位数又不是很大的情况

4. 折叠法--(了解)

折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。
折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况

5. 随机数法--(了解)
选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中random为随机数
函数。通常应用于关键字长度不等时采用此法

6. 数学分析法--(了解)
设有n个d位数,每一位可能有r种不同的符号,这r种不同的符号在各位上出现的频率不一定相同,可能在某些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀只有某几种符号经常出现。可根据散列表的大小,选择其中各种符号分布均匀的若干位作为散列地址。

数字分析法通常适合处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布较均匀的情况

综上

注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲

5.5 冲突-避免-负载因子调节(重点)

所以当冲突率达到一个无法忍受的程度时,我们需要通过降低负载因子来变相的降低冲突率。
已知哈希表中已有的关键字个数是不可变的,那我们能调整的就只有哈希表中的数组的大小.

5.6 冲突-解决

解决哈希冲突两种常见的方法是:闭散列和开散列

5.7 冲突-解决-闭散型

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。那如何寻找下一个空位置呢?

1. 线性探测
比如上面的场景,现在需要插入元素44,先通过哈希函数计算哈希地址,下标为4,因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。
线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。
      插入:

      通过哈希函数获取待插入元素在哈希表中的位置  
      如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素

采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他
元素的搜索。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标
记的伪删除法来删除一个元素。

2.二次探测

线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为: = ( + )% m, 或者:H1 = (H - i*i )% m。其中:i = 1,2,3…, 是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置,m是表的大小。 对于2.1中如果要插入44,产生冲突,使用解决后的情况为

这就是一次和二次的区别

研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容。


因此:比散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷。

5.8 冲突-解决-开散列/哈希桶(重点)

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素。
开散列,可以认为是把一个在大集合中的搜索问题转化为在小集合中做搜索了。

5.9 冲突严重时的解决办法

刚才我们提到了,哈希桶其实可以看作将大集合的搜索问题转化为小集合的搜索问题了,那如果冲突严重,就意味着小集合的搜索性能其实也时不佳的,这个时候我们就可以将这个所谓的小集合搜索问题继续进行转化,例如:
1. 每个桶的背后是另一个哈希表
2. 每个桶的背后是一棵搜索树

演示(创建桶):

class MHashMap{
    final static double Loodfactor = 0.75;
    static class Node{
        int k;
        int val;
        Node next;

        public Node(int k, int val) {
            this.k = k;
            this.val = val;
        }
    }
   Node[] array ;
    int size;

    public MHashMap() {
        array = new Node[10];
    }
    public  void put(int key, int val){
        int index = key % array.length;
        //遍历index下标的key,看是否存在key,如果存在则更新val;
       Node cur = array[index];
        while(cur != null){
            if(key == cur.val){
                cur.val = val;
                return;
            }
            cur = cur.next;
        }
        Node node = new Node(key,val);
        node.next = array[index];
        array[index] = node;
        size++;
        if(doLoodfactor()>Loodfactor){

        }
    }
    public double doLoodfactor(){
        return size*1.0/ array.length;
    }
    public void resize(){
       Node[] newarray = new Node[2* array.length];
        for (int i = 0; i < array.length ; i++) {
            Node cur = array[i];
            while(cur != null){
                int newIndex = cur.k% newarray.length;
                Node node = new Node(cur.k, cur.val);
                node.next = newarray[newIndex];
                newarray[newIndex] = node;
                cur = cur.next;
            }
        }
        array = newarray;
    }
    public int get(int key){
        int index = key % array.length;
        //遍历index下标的key,看是否存在key,如果存在则更新val;
     Node cur = array[index];
        while(cur != null){
            if(cur.k == key){
                return cur.val;
            }
            cur = cur.next;
        }
        return -1;
    }
}

虽然哈希表一直在和冲突做斗争,但在实际使用过程中,我们认为哈希表的冲突率是不高的,冲突个数是可控的,
也就是每个桶中的链表的长度是一个常数,所以,通常意义下,我们认为哈希表的插入/删除/查找时间复杂度是O(1).

6. 和Java类的关系

1. HashMap 和 HashSet 即 java 中利用哈希表实现的 Map 和 Set
2. java 中使用的是哈希桶方式解决冲突的
3. java 会在冲突链表长度大于一定阈值后,将链表转变为搜索树(红黑树)
4. java 中计算哈希值实际上是调用的类的 hashCode 方法,进行 key 的相等性比较是调用 key 的 equals 方法。所以如果要用自定义类作为 HashMap 的 key 或者 HashSet 的值,必须覆写 hashCode 和 equals 方法,而且要做到 equals 相等的对象,hashCode 一定是一致的。

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

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

相关文章

微信小程序(四十九)拦截器处理登入失败情况

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.拦截器判断处理失败与成功的情况 2.使用拦截器拒绝失败的情况&#xff0c;使网络请求后面的逻辑步骤不会执行 源码&#xff1a; index.wxml <button type"primary" bind:tap"onSubmit"…

c#打印BarTend标签提示:具名数据源没有cuckoo*具名数据(解决)

c#打印BarTend标签提示&#xff1a;具名数据源没有cuckoo*具名数据&#xff08;解决&#xff09; 今天咕咕更新打印模板的时候遇到的问题&#xff0c;就是在模版中配置了字段名&#xff0c;但是启动c#应用&#xff0c;后端发送json数据打印的时候c#报错提示&#xff0c;没有在…

【硬件工程师面经整理16_电路设计篇】

文章目录 1 画一个1.8V转3.3V升压电路。2 用NMOS设计一个双向电平转换电路&#xff0c;及原理3 三分频电路的设计 1 画一个1.8V转3.3V升压电路。 升压&#xff1a;1.8V升3.3V升压芯片方案&#xff0c;如PW5100&#xff0c;固定输出3V、3.3V&#xff0c;输入电压范围0.7V-5V&am…

202209 青少年软件编程等级考试Scratch二级真题

第 1 题 【 单选题 】 数字&#xff1a;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;6&#xff0c;9&#xff0c;13&#xff0c;19&#xff0c;28&#xff0c;...的下一项是多少&#xff1f; A&#xff1a;37 B&#xff1a;39 C&#xff1a;41 D&#xff1a;47 …

【JavaEE】_第一个SpringBoot项目

目录 1. 第一个SpringBoot项目 1.1 创建项目 1.2 加载依赖 1.3 运行启动类 1.4 创建一个简单类试运行 2. 关于SpringBoot项目的目录结构 3. 关于修改文件名 4. 关于启动日志 5. 关于访问出错 5.1 404 5.1.2 URL输入错误 5.1.2 注解错误 5.2 500 5.3 无法访问此网…

C语言基础(五)——结构体与C++引用

七、结构体与C引用 7.1 结构体的定义、初始化、结构体数组 C 语言提供结构体来管理不同类型的数据组合。通过将不同类型的数据组合成一个整体&#xff0c;方便引用 例如&#xff0c;一名学生有学号、姓 名、性别、年龄、地址等属性&#xff0c;如果针对学生的学号、姓名、年龄…

EasyExcel3.1.1版本上传文件忽略列头大小写

1、背景 项目中使用easyExcel3.1.1版本实现上传下载功能&#xff0c;相关数据DTO以 ExcelProperty(value "dealer_gssn_id") 形式规定其每一列的名称&#xff0c;这样的话easyExcel会完全匹配对应的列名&#xff0c;即用户上传文件时&#xff0c;列名写成Dealer_…

【Linux取经路】文件系统——inode与软硬链接

文章目录 一、前言二、认识硬件——磁盘2.1 磁盘的存储构成2.2 磁盘的逻辑抽象 三、操作系统对磁盘的使用3.1 再来理解创建文件3.2 再来理解删除文件3.3 再来理解目录 四、硬链接五、软链接六、结语 一、前言 在之前的【Linux取经路】文件系统之被打开的文件——文件描述符的引…

【学习心得】响应数据加密的原理与逆向思路

一、什么是响应数据加密&#xff1f; 响应数据加密是常见的反爬手段的一种&#xff0c;它是指服务器返回的不是明文数据&#xff0c;而是加密后的数据。这种密文数据可以被JS解密进而渲染在浏览器中让人们看到。 它的原理和过程图如下&#xff1a; 二、响应数据加密的逆向思路 …

go并发模式之----使用时顺序模式

常见模式之二&#xff1a;使用时顺序模式 定义 顾名思义&#xff0c;起初goroutine不管是怎么个先后顺序&#xff0c;等到要使用的时候&#xff0c;需要按照一定的顺序来&#xff0c;也被称为未来使用模式 使用场景 每个goroutine函数都比较独立&#xff0c;不可通过参数循环…

Linux入门到入土

Linxu Linux 简介 Linux 内核最初只是由芬兰人林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;在赫尔辛基大学上学时出于个人爱好而编写的。 Linux 是一套免费使用和自由传播的类 Unix 操作系统&#xff0c;是一个基于 POSIX&#xff08;可移植操作系统接口&#xff09…

常用的电阻、电容的种类和应用场合?

电阻的 a.按阻值特性:固定电阻、可调电阻、特种电阻(敏感电阻)&#xff0c;不能调节的,我们称之为固定电阻,而可以调节的,我们称之为可调电阻.常见的例如收音机音量调节的,主要应用于电压分配的,我们称之为电位器. b.按制造材料:碳膜电阻、金属膜电阻、线绕电阻&#xff0c;捷…

ElasticSearch开篇

1.ElasticSearch简介 1.1 ElasticSearch&#xff08;简称ES&#xff09; Elasticsearch是用Java开发并且是当前最流行的开源的企业级搜索引擎。能够达到实时搜索&#xff0c;稳定&#xff0c;可靠&#xff0c;快速&#xff0c;安装使用方便。 1.2 ElasticSearch与Lucene的关…

从0开始回顾Mysql --- MySQL初体验

大白话从0开始回顾MySQL&#xff0c;去除了一些繁琐的操作的演示以及内容&#xff0c;如MySQL安装等&#xff0c;本篇文章适合复习MySQL语法&#xff0c;学习MySQL语句&#xff0c;对MySQL不太熟练的同学&#xff0c;希望对大家有一些帮助。 MySQL初体验 首先&#xff0c;我将…

Linux内核MMC框架

1.mmc的概念 1.MMC MultiMedia Card&#xff0c;多媒体存储卡&#xff0c; 但后续泛指一个接口协定&#xff08;一种卡式&#xff09;&#xff0c;能符合这接口的内存器都可称作mmc储存体,工作电压&#xff1a;高电压为2.7&#xff5e;3.6 V&#xff0c;低电压为1.65&#xf…

Python——桌面摄像头软件(附源码+打包)

目录 一、前言 二、桌面摄像头软件 2.1、下载项目 2.2、功能介绍 三、打包工具&#xff08;nuitka&#xff09; 四、项目文件复制&#xff08;我全部合到一个文件里面了&#xff09; 五、结语 一、前言 看见b站的向军大叔用electron制作了一个桌面摄像头软件 但是&#x…

【应用多元统计分析】--多元数据的直观表示(R语言作图)

例1.2 为了研究全国31个省、市、自治区2018年城镇居民生活消费的分布规律&#xff0c;根据调查资料做区域消费类型划分。 指标&#xff1a; 食品x1&#xff1a;人均食品支出(元/人) 衣着x2&#xff1a;人均衣着商品支出(元/人) 居住x3&#xff1a;人均居住支出(元/人) 生活x4…

ssh无法直接登入Linux超级用户root(23/3/3更新)

说明&#xff1a;不允许ssh用超级用户的身份登入是为了安全性&#xff0c;如果只是学习使用对安全性没啥要求可以按以下操作解除限制 以普通用户登录到服务器后&#xff0c;执行以下命令以编辑 SSH 服务器配置文件 /etc/ssh/sshd_config sudo nano /etc/ssh/sshd_config 此时会…

二极管原理及典型应用电路、三极管基本结构及类型状态

目录 二极管原理及典型应用电路 二极管的工作原理 二极管保护电路 二极管整流电路 二极管稳压电路 三极管基本结构及类型状态 三极管基本结构和类型 三极管的 3 种工作状态 二极管原理及典型应用电路 如下图&#xff0c;二极管长成这样。它们通常有一个黑色圆柱体&am…

【大厂AI课学习笔记NO.60】(13)模型泛化性的评价

我们学习了过拟合和欠拟合&#xff0c;具体见我的文章&#xff1a;https://giszz.blog.csdn.net/article/details/136440338 那么今天&#xff0c;我们来学习模型泛化性的评价。 泛化性的问题&#xff0c;我们也讨论过了&#xff0c;那么如何评价模型的泛化性呢&#xff1f; …