C++ | 位图与布隆过滤器

news2024/11/17 17:55:22

目录

前言

一、位图

1、位图的引入

2、位图的实现

(1)基本结构

(2)构造函数

(3)插入数据

(4)删除数据

(5)是否存在

3、位图的优缺点

4、位图的应用

二、布隆过滤器

1、布隆过滤器的引入

2、布隆过滤器的实现

(1)布隆过滤器的框架

(2)布隆过滤器的插入

(3)布隆过滤器的查找

(4)关于布隆过滤器的删除

3、布隆过滤器的优缺点

三、哈希切割


前言

        本文主要讲解位图及位图应用,布隆过滤器及其引用,以及我们的对于海量数据的处理,如何正确理解位图与布隆过滤器的使用场景;

一、位图

1、位图的引入

        首先,我们来看看一道来自腾讯的面试题;

        1、给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。

你如果遇到这题,你会想着如何处理这题呢?

思路1:将数据放入set/unordered_set中;

        首先我们来分析一下,40亿个整型,每个整型4字节,因此总共160亿字节;160亿字节大概多少内存呢?我们都知道1KB = 1024Byte,1MB = 1024KB,1GB = 1024MB,因此我们可以推出 1GB = 1024*1024*1024Byte,大约10亿字节左右,因此160亿字节约等于16GB内存;这还仅仅只是数据占16GB内存,我们的容器里存放相应的指针也需要占用内存,明显是不合理的;故这种方案不合理

思路2:前面我们存放一个数据占用了4个字节,我们可不可以少用一些内存来存储这个数据呢?我们最低使用多大内存可以储存下这一个数据呢?想一想,我们是否可以使用一个比特位存储下这个数据,此时我们仅仅只需要0.5GB左右就可以存下这40亿的数据(以前需要一个整型的大小32比特位,现在只需要1比特位,16GB / 32 = 0.5GB),我们使用哈希映射的思想,我们给每一个数据映射到一个比特位位置,1表示这个数据存在,0表示这个数据不存在,如下图所示;我们存入一个7,我们将7对应的比特位位置置为1即可;这就是我们接下来要实现的位图的结构;

2、位图的实现

(1)基本结构

首先我们写出基本框架,具体如下图所示;

	template<size_t N>
	class bitset
	{
	public:

	private:
		std::vector<char> _bits;
	};
(2)构造函数

位图的构造函数主要是完成vector容器的初始化,我们想一想,传入N个数据的位图,我们需要开多大的空间合适;如果N为14,我们需要开2个字节空间并不合适,我们需要开三个字节的空间,即N / 8 + 1;

		// 构造
		bitset()
		{
			size_t len = N / 8 + 1;
			_bits.resize(len, 0);
		}
(3)插入数据

我们在位图中插入一个数据就是将对应的比特位置为1,我们可以通过将数据x / 8得到该数据在哪一个字节,将数据 x % 8 可以得到该数据在字节的哪一个比特位,最后通过按位或运算移位运算符将对应得比特位置为1;

		// 插入
		void set(size_t val)
		{
			// 第几个字节中
			size_t x = val / 8;
			// 字节的第几个比特位中
			size_t y = val % 8;
			// 置1
			_bits[x] |= (1 << y);
		}
(4)删除数据

位图数据得删除即将对应得比特位置为0,我们想要将对应得比特位置为零也需要得到对应比特位得位置,与插入相同的方式得到比特位的字节与字节中的位置;然后通过按位与移位运算按位取反运算将对应的比特位置为0;

		// 删除
		void reset(size_t val)
		{
			// 第几个字节中
			size_t x = val / 8;
			// 字节的第几个比特位中
			size_t y = val % 8;
			// 置0
			_bits[x] &= ~(1 << y);
		}
(5)是否存在

我们除了要实现插入与删除,我们还要提供一个测试该数据是否存在的接口;我们同样通过按位与运算查看该比特位的数据是否存在;

		// 在不在
		bool test(size_t val)
		{
			// 第几个字节中
			size_t x = val / 8;
			// 字节的第几个比特位中
			size_t y = val % 8;
            // 0是不存在,非0代表存在
			return _bits[x] & (1 << y);
		}

3、位图的优缺点

位图优缺点如下:

优点:

1、查找速度快,O(1)的时间复杂度内就能查找一个数据是否存在;

2、节省内存空间,40亿数据才占用0.5G内存

缺点:

1、只适用于整型的存储

4、位图的应用

位图主要应用于以下场景:

1、快速查找某个数据是否在一个集合中

2、排序 + 去重

3、求两个集合的交集、并集等

4、操作系统中磁盘块标记

接下来我以几道面试题来举例讲解位图的应用;

1. 给定100亿个整数,设计算法找到只出现一次的整数?

思路:题目说了100亿数据,实际上会有大量重复,因为无符号整型的最大值也只有42亿9千万左右;我们可以设计一个类,该类有连个位图,每个位置可以分别用 00 表示 该数据一次都没出现,用 01 表示该数据只出现了一次,用 10 表示该数据出现了两次及以上;最后遍历整个位图,输出 01 比特位映射的数据不就是只出现一次的数据吗?

	template<size_t N>
	class twobits1
	{
	public:
		void set(size_t val)
		{
			// 00 -> 01
			if (_bits1.test(val) == false && _bits2.test(val) == false)
			{
				_bits2.set(val);
			}// 01 -> 10
			else if (_bits1.test(val) == false && _bits2.test(val) == true)
			{
				_bits1.set(val);
				_bits2.reset(val);
			}
            // 两次及以上不必处理
		}

		void print()
		{
			for (size_t i = 0; i < N; i++)
			{
				if (_bits1.test(i) == false && _bits2.test(i) == true)
					std::cout << i << std::endl;
			}
		}
	private:
		bitset<N> _bits1;
		bitset<N> _bits2;
	};

2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

思路1:只需创建一个位图,遍历文件1,存入位图中,然后遍历文件2,每个数据与位图中对比看是否存在,存在即为交集;这里有一个问题,假如文件1的数据为 1  2  4,文件2的数据为 1  2  1   1   1,此时遍历文件2输出的结果会有大量重复的1,因此我们还需要去重,我们可以在遍历文件2的同时,第一次找到位图中的交集时,我们将这个位图中的比特位置为0,这时,在后续的查找遍历中,不会再重复输出这个数据了,便可以达到去重的效果;

思路2:创建两个位图,分别遍历这两个文件,将文件1存入位图1中,文件2存入位图2中,然后从0一直遍历到无符号整型最大值,如果对应比特位都为1时,此时便是这两个文件数据的交集;

3、1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

思路:同题目1,我们设计一个类,有两个位图,分别定义状态 00 表示该位置映射数据为0个,01表示该位置映射数据为1个,10表示该位置映射数据为两个,11表示该位置映射数据为2个及以上,遍历完文件存入位图后,我们再从0遍历到无符号整型最大值,然后输出状态为 01 或 10的位置映射的值;稍稍该该题目1的代码便可实现,具体代码如下;

	template<size_t N>
	class twobits2
	{
	public:
		void set(size_t val)
		{
			// 00 -> 01
			if (_bits1.test(val) == false && _bits2.test(val) == false)
			{
				_bits2.set(val);
			} // 01 -> 10
			else if (_bits1.test(val) == false && _bits2.test(val) == true)
			{
				_bits1.set(val);
				_bits2.reset(val);
			} // 10 -> 11
			else if(_bits1.test(val) == true && _bits2.test(val) == false)
			{
				_bits1.set(val);
				_bits2.set(val);
			}
		}

		void print()
		{
			for (size_t i = 0; i < N; i++)
			{
				if ((_bits1.test(i) == false && _bits2.test(i) == true)
					|| (_bits1.test(i) == true && _bits2.test(i) == false))
					std::cout << i << std::endl;
			}
		}
	private:
		bitset<N> _bits1;
		bitset<N> _bits2;
	};

二、布隆过滤器

1、布隆过滤器的引入

        首先,我们还是来看一道经典的面试题;

1、给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出
精确算法和近似算法

思路:此时,我们发现用我们的位图无法解决这个问题了,因为位图只针对整型家族的数据才有效,此时我们引入一个新的数据结构 ----- 布隆过滤器,其思想是将字符串通过hash函数转化成我们的整型;然后将一个文件中的query存进我们的位图中,另一个文件在布隆过滤器中查询,这就是布隆过滤器的主要思想,哈希函数+位图;也是我们这道题的近似算法;

字符串转整型哈希函数

2、布隆过滤器的实现

虽然我们使用一些哈希函数将字符串映射成我们的整型,这些哈希函数虽然可以减少冲突,但不能完全避免,为了进一步减少冲突,我们通过多个哈希函数将一个字符串映射多个位置来达到减少冲突的效果;如下面实例;

上图是使用两个哈希函数,将我们的一个字符串映射到两个位置,当然,我们在查找时,映射的两个位置必须都存在,才能表示这个字符串可能存在;

(1)布隆过滤器的框架

        关于布隆过滤器还有一个问题,就是应该有多少个哈希函数映射,这其实是数学问题了,有兴趣升入了解的可以阅读下方链接文章;

布隆过滤器哈希函数深入研究

        如上公式中,k是哈希函数个数,m是布隆过滤器长度,n是数据个数;我们通过如上公式推导出 m = (k / ln 2)* n;我们写出如下代码结构;

	struct BKDRHash
	{
		size_t operator()(const std::string& s)
		{
			size_t hash = 0;
			for(auto ch : s)
			{
				hash = hash * 131 + ch;      
			}
			return hash;
		}
	};

	struct APHash
	{
		size_t operator()(const std::string& s)
		{
			size_t hash = 0;
			for (long i = 0; i < s.size(); i++)
			{
				size_t ch = s[i];
				if ((i & 1) == 0)
				{
					hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
				}
				else
				{
					hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
				}
			}
			return hash;
		}
	};

	struct DJBHash
	{
		size_t operator()(const std::string& s)
		{
			if (s.size() == 0)
				return 0;
			register size_t hash = 5381;
			for(auto ch : s)
			{
				hash += (hash << 5) + ch;
			}
			return hash;
		}
	};

	template<size_t N, 
		class K = std::string, 
		class HashFunc1 = BKDRHash,
		class HashFunc2 = APHash,
		class HashFunc3 = DJBHash>
	class BloomFilter
	{
	public:

	private:
		static const int _a = 4;  // k / ln 2
		bitset<N * _a> _bits;
	};

        本文挑选了三个哈希函数,读者可不必一定选这三个哈希函数,可从之前字符串转整型的哈希函数中任选;本文还算出了k / ln 2 的值并命名成a;

(2)布隆过滤器的插入

        我们分别算出我们的hashi,然后对相应位置取余数,然后再插入;

		void set(const K& key)
		{
			int len = _a * N;
			size_t hashi1 = HashFunc1()(key) % len;
			size_t hashi2 = HashFunc2()(key) % len;
			size_t hashi3 = HashFunc3()(key) % len;

			_bits.set(hashi1);
			_bits.set(hashi2);
			_bits.set(hashi3);
		}
(3)布隆过滤器的查找

        布隆过滤器的查找主要查找key对应的hashi是否全都映射到了位图中;具体代码如下所示;

		bool test(const K& key)
		{
			int len = _a * N;
			size_t hashi1 = HashFunc1()(key) % len;
			size_t hashi2 = HashFunc2()(key) % len;
			size_t hashi3 = HashFunc3()(key) % len;

			if (_bits.test(hashi1) && _bits.test(hashi2) && _bits.test(hashi3))
			{
				return true;
			}
			else
			{
				return false;
			}
		}

        此外,我们还需要注意的一点是布隆过滤器的查找在的情况是不准确的,不在的情况是完全准确的;为什么呢?下图有做解释;

        如若生物并不存在,但是映射的位置是别的字符串映射的位置(因为哈希函数不能完全保证不冲突),此时查找结果却显示生物存在布隆过滤器中;因此在有可能是不准确的;而不在一定是准确的,但凡之前插入过,其映射位置一定会被设置成1,若指定位置没设置成1,则一定不存在; 

(4)关于布隆过滤器的删除

        布隆过滤器一般不会提供删除接口,如下图所示;

        若布隆过滤器提供删除接口,如若删除语文,将语文对应的比特位设置成0,此时我们查找英语和数学时,我们会发现也查不到的情况;

        若是必须提供删除接口,你有什么办法呢?

思路:我们每个映射的位置用多个比特位来储存,而不是只用一个,假设用四个比特位代表一个位置,然后我们用引用计数的思想,每插入一个哈希映射,我们对其++。比如四个比特位可以表示15,即为 1111 ;而删除一个哈希映射位置时,我们必须对其-1操作;这样就可以实现布隆过滤器的删除接口,且删除一个数据不会对别的数据产生影响;但是这种方法是有弊端的,其一是占用空间增多了,其二是引用计数的数据范围有限;

3、布隆过滤器的优缺点

布隆过滤器的优缺点如下:

优点:

1、增加与查找的时间复杂度为O(K)(K为哈希函数个数,一般很少);

2、布隆过滤器不需要存储元素本身,对于某些场合有数据安全性;

3、可以存储数据规模较大的数据群体;

4、允许小规模误判的情况下,布隆过滤器其存储数据有很大的空间优势

缺点:

1、有误判率(在有可能不准确)。

2、不能获取元素本身

3、一般情况下,不能删除布隆过滤器的元素

4、即使提供引用计数来实现删除,也可能会产生计数回绕的问题(unsigned int性质)

        下面我们还提供了一个测试布隆过滤器的准确性的测试接口;我们可以通过调节_a来控制误判率;

void test_bloomfilter()
{
	srand((unsigned int)time(0));
	const int N = 1000000;
	MySpace::BloomFilter<N> bf;
	string str1 = "https://mp.csdn.net/mp_blog/creation/editor/132078512?spm=1001.2014.3001.5352";
	// 源字符串
	vector<string> v1;
	for (int i = 0; i < N; i++)
	{
		v1.push_back(str1 + to_string(i));
	}
	for (auto& e : v1)
	{
		bf.set(e);
	}

	// 近似字符串
	vector<string> v2;
	for (int i = 0; i < N; i++)
	{
		v2.push_back(str1 + to_string(999999 + rand()));
	}
	size_t count1 = 0;
	for (auto& e : v2)
	{
		if (bf.test(e))
		{
			count1++;
		}
	}
	// 不近似字符串
	vector<string> v3;
	string str2 = "www.baidu.com";
	for (int i = 0; i < N; i++)
	{
		v3.push_back(str2 + to_string(rand() + i));
	}
	size_t count2 = 0;
	for (auto& e : v3)
	{
		if (bf.test(e))
		{
			count2++;
		}
	}
	cout << count1 << endl;
	cout << count2 << endl;
	cout << "近似字符串的重复率:" << (double)count1 / (double)N << endl;
	cout << "不近似字符串的重复率:" << (double)count2 / (double)N << endl;

}

三、哈希切割

        哈希切割是本章的一个补充话题,关于哈希切割首先看如下面试题;

1、给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?

思路:首先,我们看题目是让我们找到出现次数最多的IP地址,我们首先想到这就是一个经典的TopK问题;我们可以通过堆来找,但是堆有无法存放这么多的数据,如何处理呢?我们可以将数据通过哈希函数划分成500份文件,然后将每个文件中通过map/unordered_map得到出现次数最多的IP并记录下来,可是,我们是通过哈希函数切分,并不能保证每个文件的大小一致,仍可能出现有的文件过大;

对于过大的小文件,有如下两种情况:

1、小文件有很多重复IP;

2、小文件没有很多重复IP,仅仅是哈希切割恰好切到了一起;

我们如何区分这两种情况呢?我们可以将这个小文件插入map/unordered_map中,若是因为小文件有很多重复IP,我们可以统计出IP文件个数,因为有很多会插入失败;若是情况2,我们插入过程中,肯定会抛出bad_alloc的异常;这时我们对这个小文件换哈希函数继续切分即可;最终每个小文件肯定能统计出一个出现次数最多的IP,我们将这些IP以pair<IP, 次数>的形式放进大堆中(按次数比较),最后便可得到出现最多的IP地址;

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

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

相关文章

js-匈牙利算法

匈牙利算法 素数伴侣新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个注脚注释也是必不可少的K…

TSINGSEE青犀视频汇聚平台EasyCVR视频广场面包屑侧边栏支持拖拽操作

TSINGSEE青犀视频汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTMP、FLV、HLS、Web…

第七章:SpringMVC中

第七章&#xff1a;SpringMVC中 7.1&#xff1a;SpringMVC的视图 ​ SpringMVC中的视图是View接口&#xff0c;视图的作用渲染数据&#xff0c;将模型Model中的数据展示给用户SpringMVC视图的种类很多&#xff0c;默认有转发视图和重定向视图。 ​ 当工程引入jstl的依赖&…

react中PureComponent的理解与使用

一、作用 它是一个纯组件&#xff0c;会做一个数据的浅比较&#xff0c;当props和state没改变的时候&#xff0c;不会render重新渲染&#xff0c; 改变后才会render重新渲染&#xff0c;提高性能。 二、使用 三、注意 它不能和shouldComponentUpdate生命周期同时使用。因为它…

【网络基础进阶之路】路由器间的静态综合详解

PS&#xff1a;本实验基于华为的eNSP模拟软件进行 题目内容&#xff1a; 完成步骤&#xff1a; 1、对192.168.1.0/24进行子网划分 2、对每一个路由器进行IP的配置 3、开始静态路由的书写&#xff0c;在写之前&#xff0c;我们可以先对每一个路由器写一条通向右边的缺省路由&…

如何解决跨域问题?

一&#xff0c;什么是跨域 域&#xff08;Origin&#xff09;是由协议、域名和端口组成的&#xff0c;只有这三者完全一致的情况下&#xff0c;浏览器才会认为两个网址同源&#xff0c;否则就认为存在跨域。跨域是指在Web开发中&#xff0c;一个网页的JavaScript代码试图访问另…

机器学习实战13-超导体材料的临界温度预测与分析(决策树回归,梯度提升回归,随机森林回归和Bagging回归)

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下机器学习实战13-超导体材料的临界温度预测与分析(决策树回归,梯度提升回归,随机森林回归和Bagging回归)&#xff0c;这几天引爆网络的科技大新闻就是韩国科研团队宣称发现了室温超导材料-LK-99&#xff0c;这种材料…

读MetaGait代码

前置问题 关于分布式&#xff0c;可能是系统、网络等问题我最终还是取消掉了&#xff0c;下面这些尝试使用分布式时报的错姑且记录一下。。。 ############################## module ‘distutils’ has no attribute ‘version’ pip install setuptools59.5.0 No module …

数智保险 创新未来 | GBASE南大通用亮相中国保险科技应用高峰论坛

本届峰会以“数智保险 创新未来”为主题&#xff0c;GBASE南大通用携新一代创新数据库产品及金融信创解决方案精彩亮相&#xff0c;与国内八百多位保险公司高管和众多保险科技公司技术专家&#xff0c;就保险领域数字化的创新应用及生态建设、新一代技术突破及发展机遇、前沿科…

算法通关村—括号匹配问题解析

1. 有效的括号 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘]’ 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。…

网易云音乐扫码登录

简介 尚硅谷的网易云音乐项目无法登录&#xff0c;因为目前网易修改了接口使用手机号和密码登录的话需要先通过认证才可以&#xff0c;所以目前无法使用手机号登录&#xff0c;只能使用二维码登录&#xff0c;接下来我就教大家如何使用 二维码进行登录 实现步骤 1.获取nodejs接…

【Spring Cloud 四】Ribbon负载均衡

Ribbon负载均衡 系列文章目录背景一、什么是Ribbon二、为什么要有Ribbon三、使用Ribbon进行负载均衡服务提供者A代码pom文件yml配置文件启动类controller 服务提供者Bpom文件yml配置文件启动类controller 服务消费者pom文件yml文件启动类controller 运行测试 四、Ribbon的负载均…

Kubespray-offline v2.21.0-1 下载 Kubespray v2.22.1 离线部署 kubernetes v1.25.6

文章目录 1. 目标2. 预备条件3. vcenter 创建虚拟机4. 系统初始化4.1 配置网卡4.2 配置主机名4.3 内核参数 5. 打快照6. 安装 git7. 配置科学8. 安装 docker9. 下载介质9.1 下载安装 docker 介质9.2 下载 kubespray-offline-ansible 介质9.3 下载 kubernetes 介质 10. 搬运介质…

css, resize 拖拉宽度

效果如下&#xff1a; 可直接复制预览查看属性值: 关键样式属性&#xff1a; resize: horizontal; overflow-x: auto; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content…

睿讯微带你深度了解汽车交流充电桩

这几年随着新能源汽车的普及&#xff0c;充电桩也越来越多的出现在我们的视野中。新能源纯电汽车就好比一种大号的电子产品&#xff0c;而充电桩则是它不可缺少的子系统&#xff0c;是新能源车主们的必要选择。 汽车充电桩分为直流和交流两种&#xff0c;2022年底全国公共充电桩…

华为防火墙会话表

会话表是设备转发报文的关键表项。所以当出现业务故障时&#xff0c;通常可以通过查看会话表信息&#xff0c;大致定位发生故障的模块或阶段。 当某个业务发生问题&#xff0c;例如流量不通或者断断续续时&#xff0c;通过查看会话表可以得出以下信息&#xff1a; 如果该项业…

Android 死机问题学习笔记

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、死机系统简图二、死机的可能原因三、死机问题需要分析哪些数据四 、Java Backtrace 分析五、常见 Java backtrace 举例六、Native Backtrace七、Ke…

单月涨粉90w,小红书科普视频引发高关注

为洞察小红书平台的内容创作趋势及品牌营销策略&#xff0c;新红推出7月月度榜单&#xff0c;从创作者、品牌、品类多方面入手&#xff0c;解析月榜数据&#xff0c;为从业者提供参考。 爆款笔记涨粉90w 科普视频引发高关注 据7月的『涨粉排行榜』TOP500数据显示&#xff0c;头…

在线LaTeX公式编辑器编辑公式

在线LaTeX公式编辑器编辑公式 在编辑LaTex文档时候&#xff0c;需要输入公式&#xff0c;可以使用在线LaTeX公式编辑器编辑公式&#xff0c;其链接为: 在线LaTeX公式编辑器&#xff0c;https://www.latexlive.com/home 图1 在线LaTeX公式编辑器界面 图2 在线LaTeX公式编辑器…

IDEA用Gradle构建项目时,lombok插件无效的解决办法

Lombok 可用来帮助开发人员消除 Java 的重复代码&#xff0c;尤其是对于简单的 Java 对象&#xff08;POJO&#xff09;&#xff0c;比如说getter/setter/toString等方法的编写。它通过注解实现这一目的。 正确使用姿势 一、安装Lombok插件 菜单栏File -> Settings ->…