Map和Set详解

一、二叉搜索树搜索树

1.二叉搜索树的概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
在这里插入图片描述

2.二叉搜索树的模拟实现

import sun.reflect.generics.tree.Tree;

public class BinarySearchTree {

    static class TreeNode{
        public int val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int val){
            this.val = val;
        }
    }

    public TreeNode root = null;

    public TreeNode find(int val,TreeNode root){
        TreeNode cur = root;
        while(cur != null){
            if(cur.val<val){
                cur = cur.right;
            }
            else if(cur.val>val){
                cur = cur.left;
            }
            else{
                return cur;
            }
        }
        return null;
    }

    public void insert(int val){
        TreeNode newNode = new TreeNode(val);
        if(root == null){
            root = newNode;
            return ;
        }
        TreeNode cur = root;
        TreeNode pre= cur;
        while(cur != null){
            if(cur.val<val){
                pre = cur;
                cur = cur.right;
            }
            else if(cur.val>val){
                pre = cur;
                cur = cur.left;
            }
            else{
                return;
            }
        }
        if(val> pre.val){
            pre.right = newNode;
        }
        if(val<pre.val){
            pre.left = newNode;
        }
    }


    public void delete (int val){
        if(root == null){
            return;
        }
        TreeNode cur = root;
        TreeNode parent= cur;
        while(cur != null){
            if(cur.val<val){
                parent = cur;
                cur = cur.right;
            }
            else if(cur.val>val){
                parent = cur;
                cur = cur.left;
            }
            else{
                remove(parent,cur,val);
            }
        }
    }

    private void remove(TreeNode parent, TreeNode cur, int val) {
        if(cur.left == null){
            if(cur == root){
                root = root.right;
            }
            if(parent.left == cur){
                parent.left = cur.right;
            }
            if(cur == parent.right){
                parent.right = cur.right;
            }
        }else if(cur.right == null){
            if(cur == root){
                root = root.left;
            }
            if(parent.left == cur){
                parent.left = cur.left;
            }
            if(cur == parent.right){
                parent.right = cur.left;
            }
        }
        else{
            TreeNode target = cur.right;
            TreeNode targetParent = cur;
            while(target.left!= null){
                targetParent = target;
                target = target.left;
            }
            cur.val = target.val;
            if(target==targetParent.left){//证明此时taeget没有左子树
                targetParent.left = target.right;
            }else
                targetParent.right = target.right;
        }

    }
}

在这里插入图片描述
实际上在JDK中TreeSet 和TreeMap底层实现用的就是一颗二叉搜索树(红黑树)。

二、Map & Set

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

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

上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:

  • 根据姓名查询考试成绩
  • 通讯录,即根据姓名查询联系方式

不重复集合,即需要先搜索关键字是否已经在集合中可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,本节介绍的Map和Set是 一种适合动态查找的集合容器。

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

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

2.1 Map

2.1.1关于Map的说明

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

有一点需要注意,我们知道Map的底层是一个具有其他限制的二叉搜索树(红黑树),而这个二叉搜索树所比较的对象并不是value,而是key.举个例子来说,现在有键值对<“a”,2>、<“b”,1>,那么这两个键值对如果构建一个map的话根节点是<“a”,2>,而<“b”,1>是左子树(注意不是右子树),因为“a”比“b”大。

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

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

2.2.3 Map 的常用方法说明

在这里插入图片描述


import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class Test {
    public static void main(String[] args) {
        Map<String,Integer> treeMap = new TreeMap<>();
        treeMap.put("a",0);//添加键值对
        treeMap.put("a",1);//treeMao中key类似于唯一标识符,只能有一个对象的key是a,所以这里实际是将原来a=0改成a=1
        treeMap.put("b",2);
        treeMap.put("c",3);
        treeMap.put("d",4);
        System.out.println(treeMap);//treeMao重写了TosString方法,map输出使用{}
        System.out.println(treeMap.get("a"));//根据key返回value
        //将treeMap中的所有键值对的key都放入Set中
        Set<String> keyset = treeMap.keySet();//set输出使用【】
        System.out.println(keyset);
        //将treeMap中所有的键值对都放入一个set中
        Set<Map.Entry<String,Integer>> set = treeMap.entrySet();
        System.out.println(set);
        for (Map.Entry<String,Integer> entry:set) {
            System.out.println("key="+entry.getKey()+"   value="+entry.getValue());
        }
        System.out.println(treeMap.getOrDefault("e",1));
        //如果treeMap中有"e“就返回value,如果没有就返回1

    }
}

在这里插入图片描述

注意:

  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删除掉,然后再来进行重新插入。

2.2 Set 的说明

Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key。
在这里插入图片描述
注意:

  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可以。

三、哈希表

3.1 概念

顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(logN),搜索的效率取决于搜索过程中元素的比较次数.
理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函 数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

当向该结构中:

  • 插入元素
    根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
  • 搜索元素
    对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若 关键码相等,则搜索成功

该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)
例如:数据集合{1,7,6,4,5,9};
哈希函数设置为:hash(key) = key % capacity; capacity为存储元素底层空间总的大小。
在这里插入图片描述
用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快 。
那么如果此时我们的key是14,那么14%10==4,发现也是要存放到下标为4的位置的,但是此时下标为4的位置已经有元素了呀?这种现象就叫做冲突。

3.2 冲突-概念

不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。
把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。

3.2 冲突-避免

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

3.2.1哈希函数设计

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

  1. 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间
  2. 哈希函数计算出来的地址能均匀分布在整个空间中
  3. 哈希函数应该比较简单

常见哈希函数

  • 直接定址法–(常用)
    取关键字的某个线性函数为散列地址:
    Hash(Key)= A*Key + B
    优点:简单、均匀 缺点:需要事先知道关键字的分布情况
    使用场景:适合查找比较小且连续的情况
    面试题:字符串中第一个只出现一次字符
    https://editor.csdn.net/md/?articleId=130651358
  • 除留余数法–(常用)
    设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,
    按照哈希函数: Hash(key) = key% p(p<=m),将关键码转换成哈希地址
  • 平方取中法–(了解)
    假设关键字为1234,对它平方就是1522756,抽取中间的3位227作为哈希地址; 再比如关键字为4321,对 它平方就是18671041,抽取中间的3位671(或710)作为哈希地址 平方取中法比较适合:不知道关键字的分布,而位数又不是很大的情况
  • 折叠法–(了解)
    折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和, 并按散列表表长,取后几位作为散列地址。
    折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况
  • 随机数法–(了解)
    选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中random为随机数函数。
    通常应用于关键字长度不等时采用此法
  • 数学分析法–(了解)
    设有n个d位数,每一位可能有r种不同的符号,这r种不同的符号在各位上出现的频率不一定相同,可能在某 些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀只有某几种符号经常出现。可根据 散列表的大小,选择其中各种符号分布均匀的若干位作为散列地址。
    例如:
    假设要存储某家公司员工登记表,如果用手机号作为关键字,那么极有可能前7位都是 相同的,那么我们可以 选择后面的四位作为散列地址,如果这样的抽取工作还容易出现 冲突,还可以对抽取出来的数字进行反转(如 1234改成4321)、右环位移(如1234改成4123)、左环移位、前两数与后两数叠加(如1234改成12+34=46)等方 法。数字分析法通常适合处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布较均 匀的情况

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

散列表的载荷因子定义为: α = 填入表中的元素个数/散列表的长度
α 是散列表装满程度的标志因子。由于表长是定值,α 与“填入表中的元素个数”成正比,所以,α 越大,表明填入表中的元素越多,产生冲突的可能性就越大;反之,α 越小,标明填入表中的元素越少,产生冲突的可能性就越小。实际上,散列表的平均查找长度是载荷因子α 的函数,只是不同处理冲突的方法有不同的函数。

对于开放定址法,荷载因子是特别重要因素,应严格限制在0. 7-0. 8以下。超过0. 8,查表时的CPU缓存不命中( cachemissing)按照指数曲线上升。因此,一些采用开放定址法的hash库,如Java的 系统库限制了荷载因子为0.75,超过此值将resize散列表。

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

3.4 冲突-解决

3.4.1闭散列(开放定址法)

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

  1. 线性探测
    比如上面的场景,现在需要插入元素44,先通过哈希函数计算哈希地址,下标为4,因此44理论上应该插在该 位置,但是该位置已经放了值为4的元素,即发生哈希冲突。
    线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止,插入
    通过哈希函数获取待插入元素在哈希表中的位置,如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到 一个空位置,插入新元素。
    在这里插入图片描述

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

  1. 二次探测
    线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为
    在这里插入图片描述
    或者:
    在这里插入图片描述

其中:i = 1,2,3…, H0是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置, m是表的大小。 对于3.3.1中如果要插入44,产生冲突,使用解决后的情况为:
在这里插入图片描述
闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷。

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

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

实际上,开散列的方式是链表+数组+红黑树的配置。
红黑树体现在:当链表长度>=8,并且数组长度>=64时,底层会直接重新组织数据,形成红黑树。java的hashMap就是这种结构
开散列,可以认为是把一个在大集合中的搜索问题转化为在小集合中做搜索了,时间复杂度可以认为是O(1)(因为数组是随机访问,我们认为链表的长度是一个常数)

3.5 HashCode

我们之前所讲的哈希表的值都是整型的,但是实际上当用到HashMap或者HashSet的时候,他里面存的可能是String类型,可能是一个我们定义的对象,那么我们怎么将其映射到我们的哈希表里呢?这就用到了HashCode.
在java中,我们每生成一个对象,他的地址是唯一的,所以我们可以通过地址来映射出每一个对象的唯一标识HashCode,HashCode,我们就可以根据这个对象的HashCode值结合散列函数和一系列解决、避免冲突的方法,将其映射到哈希表中从而达到我们的目的。
关于hashcode,可以去看这篇博客
https://blog.csdn.net/qq_46940224/article/details/124384187


```java
import java.util.Set;
import java.util.TreeSet;

public class Test2 {
    public static void main(String[] args) {
        Person person1 = new Person("张三",4);
        Person person2 = new Person("李四",5);
        System.out.println(person1.hashCode());
        System.out.println(person2.hashCode());
    }
    static class Person{

        String name;
        int age;

        public Person(String name, int age){
            this.name = name;
            this.age = age;
        }
    }
}

在这里插入图片描述
面试题:两个对象,hashCode一样,equals也一样吗:equals一样,hashCode一样吗?
首先要要注意的是equals如果是引用类型调用的话,比较的是这两个引用类型的地址;hashcode相同只能说明地址经过映射之后的值相同,不能说明地址相同,也就不能说equals相同了。如果equals相同(没有重写)了,那么证明地址相同,本身就是同一对象,那么当然hashcode当然相同了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

再JAVA中如何使用qsort对类进行排序?

目录 结论&#xff1a; 解析&#xff1a; 结论&#xff1a; import java.util.Arrays;class Person implements Comparable<Person>{public String name;public int age;public Person(String name, int age) {this.name name;this.age age;}Overridepublic Stri…

win10 下运行 npm run watch-poll问题

背景&#xff1a;在本地练习laravel项目&#xff0c;windows 宝塔环境&#xff08;之前装过ubuntu子系统&#xff0c;很慢&#xff0c;就放弃了。有知道的兄弟说下&#xff0c;抱拳&#xff09;。以下命令我是在本地项目中用git bash里运行的&#xff0c;最好用管理员权限打开你…

CSS自学框架之动画

这一节&#xff0c;自学CSS动画。主要学习了淡入淡出、淡入缩放、缩放、移动、旋转动画效果。先看一下成果。 优雅的过渡动画&#xff0c;为你的页面添加另一份趣味&#xff01; 在你的选择器里插入 animation 属性&#xff0c;并添加框架内置的 keyframes 即可实现&#xff0…

实例041 获取桌面大小

实例说明 获取桌面分辨率可以使用API函数GetDeviceCaps&#xff0c;但API函数参数较多&#xff0c;使用不方便&#xff0c;如何更方便的获取桌面分辨率呢&#xff1f;在本例中&#xff0c;通过读取Screen对象的属性&#xff0c;来获取桌面分辨率信息&#xff0c;以像素为单位。…

温故知新之:接口和抽象类有什么区别?

本文以下内容基于 JDK 8 版本。 1、接口介绍 接口是 Java 语言中的一个抽象类型&#xff0c;用于定义对象的公共行为。它的创建关键字是 interface&#xff0c;在接口的实现中可以定义方法和常量&#xff0c;其普通方法是不能有具体的代码实现的&#xff0c;而在 JDK 8 之后&…

前端单点登录SSO面试回答

JWT鉴权机制 1.JWT用于登录身份验证 2.用户登录成功后&#xff0c;后端通过JWT机制生成一个token&#xff0c;返回给客户端 3.客户端后续的每次请求都需要携带token&#xff0c;放在header的authorization中 4.后端从authorization中拿到token后&#xff0c;通过secretKey进…

【报错】yarn --version Unrecognized option: --version Error...

文章目录 问题分析解决问题 在使用 npm install -g yarn 全局安装 yarn 后,查看yarn 的版本号,报错如下 PS D:\global-data-display> yarn --version Unrecognized option: --version Error: Could

激活函数总结(十六):激活函数补充(SReLU、BReLU)

激活函数总结&#xff08;十六&#xff09;&#xff1a;激活函数补充 1 引言2 激活函数2.1 S-shaped Rectified Linear Activation Unit&#xff08;SReLU&#xff09;激活函数2.2 Bipolar Rectified Linear Unit (BReLU)激活函数 3. 总结 1 引言 在前面的文章中已经介绍了介绍…

继承中的构造与析构

思考 如何初始化父类成员&#xff1f; 父类构造函数和子类构造函数有什么关系&#xff1f; 子类对象的构造 子类中可以定义构造函数 子类构造函数 必须对继承而来的成员进行初始化 直接通过初始化列表或者赋值的方式进行初始化调用父类构造函数进行初始化 父类构造函数在子…

uniapp app 实现右上角回首页;点homeButton返回上一页;onNavigationBarButtonTap不生效问题

场景&#xff1a; app&#xff0c;Android移动端 实现点击右上角图标&#xff0c;回首页。 问题&#xff1a;用了官网的 homeButton&#xff0c;图标正常展示了&#xff0c;也可点击&#xff0c;但每次点击后是会返回上一页而非首页。 后来查到说&#xff0c;要结合onNavigatio…

Azure Bastion的简单使用

什么是Azure Bastion Azure Bastion 是一个提供安全远程连接到 Azure 虚拟机&#xff08;VM&#xff09;的服务。传统上&#xff0c;访问 VM 需要使用公共 IP 或者设立 VPN 连接&#xff0c;这可能存在一些安全风险。Azure Bastion 提供了一种更安全的方式&#xff0c;它是一个…

CDH集群离线配置python3环境,并安装pyhive、impyla、pyspark

背景&#xff1a; 项目需要对数仓千万级数据进行分析、算法建模。因数据安全&#xff0c;数据无法大批量导出&#xff0c;需在集群内进行分析建模&#xff0c;但CDH集群未安装python3 环境&#xff0c;需在无网情况下离线配置python3环境及一系列第三方库。 采取策略&#xf…

国产化kylin配置网络不通解决方案—1

kylin镜像自行官网下载 kylin系统安装外网不通配置 进入vi /etc/sysconfig/network-scripts/ifcfg-ens33 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic DEFROUTEyes IPV4_FAILURE_FATALno IPV6INITyes IPV6_AUTOCONFyes IPV6_DEFROUTEyes IPV6_FAILURE_FAT…

c++ 友元 运算符重载详解

友元 c是面向对象的&#xff0c;目的之一&#xff1a;封装 封装&#xff1a; 优点之一&#xff0c;就是安全。 缺点&#xff1a;在某些特殊的场合&#xff0c;不是很方便。 华为与IBM 40亿的咨询故事 IBM需要对华为各级部门做深度咨询分析&#xff0c; 为了提高咨询效率&a…

【C语言进阶(4)】指针和数组笔试题

文章目录 Ⅰ 一维数组Ⅱ 字符数组题型 1题型 2题型 3 Ⅲ 二维数组 数组名的意义 sizeof(数组名)&#xff0c;这里的数组名表示整个数组&#xff0c;计算的是整个数组的大小。&数组名&#xff0c;这里的数组名表示的是整个数组&#xff0c;取出的是整个数组的地址。除了上述…

QTreeWidget——信号处理

文章目录 基本属性信号一、信号种类二、信号测试1、currentItemChanged、itemCollapsed、itemExpanded三个信号的测试2、itemActivated信号3、 itemChanged信号4、其余信号的测试代码&#xff08;包含以上代码&#xff09; 基本属性 信号 一、信号种类 //当前项发生变化时触…

JVM——类加载与字节码技术—类文件结构

由源文件被编译成字节码文件&#xff0c;然后经过类加载器进行类加载&#xff0c;了解类加载的各个阶段&#xff0c;了解有哪些类加载器&#xff0c;加载到虚拟机中执行字节码指令&#xff0c;执行时使用解释器进行解释执行&#xff0c;解释时对热点代码进行运行期的编译处理。…

C#__基本特性和使用

// 特性&#xff08;attribute&#xff09;: // 一种允许我们向程序集添加元数据的语言结构 // 用于保存程序结构信息的某种特殊类型的类 // 类似“批注”&#xff0c;用于解释说明 #define IsShowMessage // 宏定义&#xff0c;在开头定义&#xff0…

AutoCompany模型的概念设计,涵盖了AI智能公司的各个角色

AutoCompany模型的概念设计&#xff0c;涵盖了AI智能公司的各个角色 自动化企业概念设计与设想&#xff0c;文本将介绍AutoCompany模型的概念设计&#xff0c;涵盖了AI智能公司的各个角色&#xff0c;并结合了GPT-4接口来实现各个角色的功能&#xff0c;设置中央控制器&#xf…

5、Spring_DI注解开发

DI 注解开发 1.目前面临问题 建立 mapper public interface EmployeeMapper {void save(); }建立 mapper 实现类 Repository public class EmployeeMapperImpl implements EmployeeMapper {public void save(){System.out.println("保存员工信息");} }建立 service …