vector类的模拟实现
实现基本的vector框架
参考的是STL的一些源码,实现的vector也是看起来像是一个简略版的,但是看完能对vector这个类一些接口函数更好的认识。
我们写写成员变量,先来看看STL的成元变量是那些
namespace tjl
{template<class T>class vector{public:typedef T* iterator;vector():_start(nullptr),_finish(nullptr),_eof(nullptr){}private:iterator _start;//iterator _finish;iterator _eof;//表示的是end_of_storage};}
这里的成员变量有些不一样,我们也用iterator来表示,其实这里就是一个指针,但是用迭代器的名称来称呼它,还有我们这里加上了模板,更加凸显了vector的不一样,这样的好处就是我们这个vector这个类就可以去适应各种不同的类型,达到一个____效果(填一个四字成语)。
实现构造函数
构造函数的意义就是来进行初始化,因为我们构造函数里面都是会走初始化列表的这个过程的,所以我们就可以写成这个样子。
vector():_start(nullptr),_finish(nullptr),_eof(nullptr){}
但是要注意的是我们的库里面的构造函数可不是只有这些,大家可以也来查询我们的网站来查看
vector构造函数
我们可以看到他是可以支持迭代器进行构造的,也就是说我们这里可以使用任意类型来进行构造
也可以用给值的方式,所以vector的作用还是很大的。
析构函数的实现
析构函数的作用就是在我们程序结束的时候对我们的空间进行释放,所以我们可以这样写
~vector(){if (_start){delete[]_start;_start = _finish = _eof = nullptr;}}
这里我们可以增加if这个判断,因为_start可能是为空的,所以我们可以写一个对它进行检查的功能。
实现vector的size()和capacity()
size_t size()const{return _finish - _start;}size_t capacity const{return _eof - _start;}
我们这里的实现细节其实只要注意的是const,因为我们可能是const对象进行调用,所以这里可以加上const,防止我们的权限进行放大。
实现push_back
这里的实现会有点小细节值的我们注意,我们先来想想push_back是在尾部插入数据·就可以了,但是我们可不可能如果当我们的空间是满的时候,就会遇到需要扩容的问题,所以这个时候我们需要先来写一个reserve的函数来进行扩容
reserve函数的实现
因为我们每次扩容的时候到要用到这个函数,比如尾插还是随机插入都会用到,那我们的思路是那些还有注意事项呢?
首先我们得先知道一个问题就是我们当什么时候才是要扩容的时候,因为reserve会传一个参数n
表示我们得开多大的空间,所以我们需要做的时候就是先判断要不要开这么大的空间。
先来看我们的代码
void reserve(size_t n){if (n > capacity()){T* tmp = new T[n];size_t pos = _finish - _start;if (_start){memcpy(tmp, _start, sizeof(T) * size());delete[]_start;}_start = tmp;_finish = _start + pos;_eof = _start + n;}}
思路就是我们重新开辟一块空间,然后把原来空间的内容拷贝到新内容上,这里要注意的是当我们赋值的时候,就是给_start和_finish给值的时候要先记录pos位置,因为我们扩容的时候是重新开辟的,可能存在_finish进行赋值的时候它还是指向空,这样就出现空指针的现象了。
那我们实现reserve之后就可以继续来实现push_back函数了。
void push_back(const T& val){if (_finish == _eof){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = val;++_finish;}
先检查空间是不是够,如果空间不够我们就需要进行扩容,其次就是大家这里要明白一个点,为什么我们可以对_finish进行解引用,然后直接进行给值,原因就是val的类型。
实现operator[]
这个很简单直接一把过了。
const T& operator[](size_t pos) const{return _start[pos];}
用[]进行遍历来看看。
void test1()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (int i = 0; i < v.size(); i++){cout << v[i] << " ";}
}
迭代器的实现
iterator begin(){return _start;}iterator end(){return _finish;}
因为迭代器我们必须要给它命名begin和end要不然就不能使用范围for来进行遍历,我们现在可以用迭代器来对我们的数据进行遍历,也能使用范围for,这里两种方式都写出来给大家看看。
void test1(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (int i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;for (auto e : v){cout << e << " ";}auto it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;}
这样有三种方式可以对我们进行遍历了,完成迭代器只后需要来处理一些细节问题,还是我们权限放大的问题,因为当我们用const的对象去调用的时候就不行了,这里只需要在写一个const版本的🆗了,我们可以先typedef一下。
typedef const T* const_iterator;
这样就表示这个迭代器是const修饰过的迭代器。那它的begin和end就是这样写的。
const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}
不过我们对const的迭代器只能进行读的操作就不能进行写操作了。
迭代器失效问题
这个问题是出现在insert和erase的时候是会出现问题
首先我们实现insert需要注意的细节有哪些
和我们之前遇到的顺序表其实本质是没有多大区别的,这里用的是指针,而不是数组下标,因为这里模拟实现的时候,其实我们的迭代器就是原生指针,虽然和VS里的不一样,Vs里的iterator并不是原生指针,所以很好实现我们这里的insert,来看看代码吧。
iterator insert(iterator pos, const T& val = T()){assert(pos >= begin());assert(pos <= end());size_t len = pos - _start;if (_finish == _eof){reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = val;++_finish;return pos;}
其实这里我就一步到位了,但是还是有很多的细节值的我们去注意,第一个就是为什么说这里会有迭代器失效的问题,首先就是如果我们步扩容的时候迭代器是不会失效的,但是扩容之后的pos还是指向原来控空间的,因为我们这里的扩容的步骤是开空间,然后对我们的内容进行值拷贝,释放之前的空间,这样pos就是一个野指针,所以我们需要做的就是更新pos位置到新的空间上,可以先记住pos的相对位置,然后更新。所以这里这个坑是没有位置的,还有一个需要注意的是我们需要更新外面的pos,因为形参的改变是不会影响实参的改变的,所以这里的一个重要的步骤就是返回pos的值。
迭代器的失效:第一个重要的原因就是这个指针变成野指针了,我们需要更新它
第二个原因就是当我们进行insert的时候需要进行返回,因为我们在这个函数内改变了,但是在外面还是没有进行改变(形参的改变是不会影响实参的改变的)
我们来进行测试一下
void test2(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v){cout << e << " ";}cout << endl;auto pos = find(v.begin(), v.end(), 2);v.insert(pos, 100);for (auto e : v){cout << e << " ";}}
发现最后的结果也是正确的,这样还要提醒大家,我们认为insert和erase之后的迭代器是失效的,不再使用,虽然我们可以接受pos位置,但是最好还是不要使用。
实现erase
iterator erase(iterator pos){assert(pos >= begin());assert(pos < end());iterator start = pos;while (start < _finish){*start = *(start + 1);start++;}_finish--;return pos;}
这个erase也是很简单,就是移动进行数据的覆盖就可以解决问题了。我们也来测试一下看看结果是不是对的。
void test3(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v){cout << e << " ";}cout << endl;auto pos = find(v.begin(), v.end(), 2);v.erase(pos);for (auto e : v){cout << e << " ";}cout << endl;}
深浅拷贝问题
先来实现拷贝构造。
拷贝构造的实现可以有现代写法和原始写法,我们先来写原始写法那个,然后引出我们的深浅拷贝的问题。
vector(const vector<T>& v){reserve(v.capacity());memcpy(_start, v._start, sizeof(T) * v.size());}
我们这里进行的值拷贝,是按照字节的拷贝的,如果我们T是内置类型的时候,我们的的代码是不会出现问题的,但是如果是string或者里面还是一个vector<int>的时候代码就会出现问题,我们可以先来看看string的结果。
void test4(){vector<string> v;v.push_back("111111");v.push_back("111111");v.push_back("111111");for (auto e : v){cout << e << " ";}cout << endl;vector<string> v1(v);for (auto e : v1){cout << e << " ";}}
我们这样写的时候就是会出现错误,原因是我们这里的拷贝构造是进行的值拷贝,不过如果我们不写这个拷贝构造的时候是不会出现问题的,原因string会去调用它自己的拷贝构造。
但是其根本原因还是我们扩容的是进行的是值拷贝和我们没有写拷贝构造造成的问题。现在来修改一下reserve。
void reserve(size_t n){if (n > capacity()){T* tmp = new T[n];size_t pos = _finish - _start;if (_start){//memcpy(tmp, _start, sizeof(T) * size());for (size_t i = 0; i < size(); i++){tmp[i] = _start[i];}delete[]_start;}_start = tmp;_finish = _start + pos;_eof = _start + n;}}
然后加上我们的考拷贝构造。
vector(const vector<T>& v){reserve(v.capacity());for (size_t i = 0; i < v.size(); i++){_start[i] = v._start[i];}_finish = _start + v.size();_eof = _start + v.capacity();}
这个就是原始的写法,这样我们的代码是没有问题的,但是这里为什么没有问题的第一个原因就是string是我们库里面的,进行赋值的时候会去调用它的赋值重载,但是如果我们这里的类型是vector<vector<int>>又会出现问题,我们可以来看看,如果我们需要拷贝一个杨辉三角的时候,会不会有问题。
下面来演示一下,我们可以先把杨辉三角的代码直接先拿过来
class Solution {public:vector<vector<int>> generate(int numRows) {vector<vector<int>> vv;vv.resize(numRows, vector<int>());//进行初始化//进行的是每行初始化,因为这里表示的是顺序表里面是个顺序表for (int i = 0; i < vv.size(); i++)//初始化没列{vv[i].resize(i + 1, 0);vv[i][0] = vv[i][vv[i].size() - 1] = 1;}for (int i = 0; i < vv.size(); i++){for (int j = 0; j < vv[i].size(); j++){if (vv[i][j] == 0){vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];}}}return vv;}};
然后进行拷贝一个杨辉三角,看看有没有什么问题
void test5(){vector<vector<int>> v = Solution().generate(5);auto v1(v);for (int i = 0; i < v1.size(); i++){for (int j = 0; j < v1[i].size(); j++){cout << v1[i][j] << " ";}cout << endl;}}
熟悉的感觉,真是太美妙了
那我们其实可以通过调试来看看问题所在的地方。
可以看到什么不一样的地方,一个就是我们的外面的大vector是完成了深拷贝,里面还是没有,为什么其他类型的string就可以,因为string有它自己的赋值重载,我们这里没有写vector的赋值重载,所以才会有这样的问题,那我们只需要写一个赋值重载就可以解决问题了。
void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_eof, v._eof);}vector<T>& operator=( vector<T>& v){swap(v);return *this;}
这样我们的问题就能够很好的解决了。
那我们再来完善一下其他的接口函数就解决了。
pop_back的实现
void pop_back(){erase(--end());}
template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}
完整的代码实现
#pragma once
#include<assert.h>
namespace tjl
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;vector():_start(nullptr),_finish(nullptr),_eof(nullptr){}vector(size_t n, const T& val = T()):_start(nullptr), _finish(nullptr), _eof(nullptr){reserve(n);while (n--){push_back(val);}}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_eof, v._eof);}vector<T>& operator=( vector<T> v){swap(v);return *this;}vector(int n, const T& val = T()):_start(nullptr), _finish(nullptr), _eof(nullptr){reserve(n);while (n--){push_back(val);}}template<class inputiterator>vector(inputiterator first, inputiterator last): _start(nullptr), _finish(nullptr), _eof(nullptr){while (first != last){push_back(*first);++first;}}vector(const vector<T>& v): _start(nullptr), _finish(nullptr), _eof(nullptr){reserve(v.capacity());for (size_t i = 0; i < v.size(); i++){_start[i] = v._start[i];}_finish = _start + v.size();_eof = _start + v.capacity();}void reserve(size_t n){if (n > capacity()){T* tmp = new T[n];size_t pos = _finish - _start;if (_start){//memcpy(tmp, _start, sizeof(T) * size());for (size_t i = 0; i < size(); i++){tmp[i] = _start[i];}delete[]_start;}_start = tmp;_finish = _start + pos;_eof = _start + n;}}iterator begin(){return _start;}iterator end(){return _finish;}const T& operator[](size_t pos) const{return _start[pos];}T& operator[](size_t pos) {return _start[pos];}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}void resize(size_t n, const T& val = T()){if (n < size()){_finish = _start + n;}else {reserve(n);while (_finish != _start + n){*_finish = val;_finish++;}}}void push_back(const T& val){if (_finish == _eof){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = val;++_finish;}size_t size()const{return _finish - _start;}size_t capacity() const{return _eof - _start;}iterator insert(iterator pos, const T& val = T()){assert(pos >= begin());assert(pos <= end());size_t len = pos - _start;if (_finish == _eof){reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = val;++_finish;return pos;}iterator erase(iterator pos){assert(pos >= begin());assert(pos < end());iterator start = pos;while (start < _finish){*start = *(start + 1);start++;}_finish--;return pos;}~vector(){if (_start){delete[]_start;_start = _finish = _eof = nullptr;}}private:iterator _start;//iterator _finish;iterator _eof;//表示的是end_of_storage};class Solution {public:vector<vector<int>> generate(int numRows) {vector<vector<int>> vv;vv.resize(numRows, vector<int>());//进行初始化//进行的是每行初始化,因为这里表示的是顺序表里面是个顺序表for (int i = 0; i < vv.size(); i++)//初始化没列{vv[i].resize(i + 1, 0);vv[i][0] = vv[i][vv[i].size() - 1] = 1;}for (int i = 0; i < vv.size(); i++){for (int j = 0; j < vv[i].size(); j++){if (vv[i][j] == 0){vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];}}}return vv;}};void test1(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (int i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;for (auto e : v){cout << e << " ";}auto it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;}void test2(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v){cout << e << " ";}cout << endl;auto pos = find(v.begin(), v.end(), 2);v.insert(pos, 100);for (auto e : v){cout << e << " ";}}void test3(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v){cout << e << " ";}cout << endl;auto pos = find(v.begin(), v.end(), 2);v.erase(pos);for (auto e : v){cout << e << " ";}cout << endl;}void test4(){vector<string> v;v.push_back("111111");v.push_back("111111");v.push_back("111111");for (auto e : v){cout << e << " ";}cout << endl;vector<string> v1(v);for (auto e : v1){cout << e << " ";}}void test5(){vector<vector<int>> v = Solution().generate(5);auto v1(v);for (int i = 0; i < v1.size(); i++){for (int j = 0; j < v1[i].size(); j++){cout << v1[i][j] << " ";}cout << endl;}}}
今天的分享就到这里了,我们下次再见!!!!
相关文章:

vector类的模拟实现
实现基本的vector框架 参考的是STL的一些源码,实现的vector也是看起来像是一个简略版的,但是看完能对vector这个类一些接口函数更好的认识。 我们写写成员变量,先来看看STL的成元变量是那些 namespace tjl {template<class T>class …...

Topaz Photo AI for Mac v2.3.1 补丁版人工智能降噪软件无损放大
想要将模糊的图片变得更加清晰?不妨试试Topaz Photo AI for Mac 这款人工智能、无损放大软件。Topaz Photo AI for Mac 一款强大的人工智能降噪软件,允许用户使用复杂的锐化算法来提高图像清晰度,还包括肖像编辑选项,如面部重塑、…...

【Unity3D小技巧】Unity3D中UI控制解决方案
推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 在开发中总是会控制UI界面,如何优雅的控制UI界面是…...

【状态管理一】概览:状态使用、状态分类、状态具体使用
文章目录 一. 状态使用概览二. 状态的数据类型1. 算子层面2. 接口层面2.1. UML与所有状态类型介绍2.2. 内部状态:InternalKvState 将知识与实际的应用场景、设计背景关联起来,这是学以致用、刨根问底知识的一种直接方式。 本文介绍 状态数据管理&#x…...

SQL--多表查询
我们之前在讲解SQL语句的时候,讲解了DQL语句,也就是数据查询语句,但是之前讲解的查询都是单 表查询,而本章节我们要学习的则是多表查询操作,主要从以下几个方面进行讲解。 多表关系 项目开发中,在进行数据…...

多维时序 | Matlab实现CNN-RVM卷积神经网络结合相关向量机多变量时间序列预测
多维时序 | Matlab实现CNN-RVM卷积神经网络结合相关向量机多变量时间序列预测 目录 多维时序 | Matlab实现CNN-RVM卷积神经网络结合相关向量机多变量时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现CNN-RVM卷积神经网络结合相关向量机多变量时间序…...

RK3568平台 安卓hal3适配usb camera
一.RK安卓hal3 camera框架 Camera hal3 在 android 框架中所处的位置如上图, 对上,主要实现 Framework 一整套 API 接口,响应其 控制命令,返回数据与控制参数结果。 对下, 主要是通 V4l2 框架实现与 kernel 的交互。3a…...

使用 Visual Studio Code 在远程计算机上调试 PostgreSQL
使用 Visual Studio Code 在远程计算机上调试 PostgreSQL 1. 概述 PostgreSQL 是一个功能强大的开源关系数据库管理系统,适用于各种应用程序。在开发过程中,调试 PostgreSQL 对于识别和解决问题至关重要。在本博客中,我们将手把手教你使用客…...

javascript设计模式之建造者
工厂模式不关心过程,只关心结果,这与建造者相反,建造者更关心的是过程, 这里我们创建一个基类,其拥有技能跟爱好两个属性,还有两个实例方法用来获取技能跟爱好 // 基类 let Human function (param {}) …...

安擎科技携手华为云区块链共同打造安全天空
当前,低空经济崛起,无人机多并发、混合运行时引发的网络信息安全、空域安全问题已成行业首要课题。 在2024年1月正式实施的《民用无人驾驶航空器运行安全管理规则》(CCAR-92)第549条中规定,“无人驾驶航空器航行服务提…...

学习数据结构的第一天
结构体 如何定义结构体 1、先定义结构体类型,再定义结构体类型变量 struct student/定义学生结构体类型/ { long number; char name[20]; char sex; int age; float score[3];/三科考试成绩/ }2、定义结构体类型同时定义结构体类型变量 struct student/定义学生结…...

5.electron之主进程起一个本地服务
如果可以实现记得点赞分享,谢谢老铁~ Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 Electron 将 Chromium 和 Node.js 嵌入到了一个二进制文件中,因此它允许你仅需一个代码仓库,就可以撰写支持 Windows、…...

爬取58二手房并用SVR模型拟合
目录 一、前言 二、爬虫与数据处理 三、模型 一、前言 爬取数据仅用于练习和学习。本文运用二手房规格sepc(如3室2厅1卫)和二手房面积area预测二手房价格price,只是练习和学习,不代表任何实际意义。 二、爬虫与数据处理 import requests import cha…...

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之RichText组件
鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之RichText组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、RichText组件 鸿蒙(HarmonyOS)富文本组件,…...

7.electron之渲染线程发送事件,主进程监听事件
如果可以实现记得点赞分享,谢谢老铁~ Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 Electron 将 Chromium 和 Node.js 嵌入到了一个二进制文件中,因此它允许你仅需一个代码仓库,就可以撰写支持 Windows、…...

thinkphp6入门(19)-- 中间件向控制器传参
可以通过给请求对象赋值的方式传参给控制器(或者其它地方),例如 <?phpnamespace app\middleware;class Hello {public function handle($request, \Closure $next){$request->hello ThinkPHP;return $next($request);} } 然后在控制…...

Flink Format系列(2)-CSV
Flink的csv格式支持读和写csv格式的数据,只需要指定 format csv,下面以kafka为例。 CREATE TABLE user_behavior (user_id BIGINT,item_id BIGINT,category_id BIGINT,behavior STRING,ts TIMESTAMP(3) ) WITH (connector kafka,topic user_behavior…...

Spring Data Envers 数据审计实战2 - 自定义监听程序扩展审计字段及字段值
上篇讲述了如何在Spring项目中集成Spring Data Envers做数据审计和历史版本查看功能。 之前演示的是业务表中已有的字段进行审计,那么如果我们想扩展审计字段呢? 比如目前对员工表加入了Audited审计,员工表有个字段为dept_id,为…...

一个 SpringBoot 项目能同时处理多少请求?
目录 1 问题分析 2 Demo 3 答案 4 怎么来的? 5 标准答案及影响参数一Tomcat配置 6 影响参数二 Web容器 7 影响参数三 Async 1 问题分析 一个 SpringBoot 项目能同时处理多少请求? 不知道你听到这个问题之后的第一反应是什么? 我大概…...

计算机网络——网络
计算机网络——网络 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU)前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家, [跳转到网站](https://www.captainbed.cn/qianqiu) 无线网络和移动网…...

C语言探索:选择排序的实现与解读
当我们需要对一组数据进行排序时,选择排序(Selection Sort)是一种简单但效率较低的排序算法。它的基本思想是每次从未排序的数据中选择最小(或最大)的元素,然后将其放置在已排序序列的末尾。通过重复这个过…...

Golang 学习(二)进阶使用
二、进阶使用 性能提升——协程 GoRoutine go f();一个 Go 线程上,可以起多个协程(有独立的栈空间、共享程序堆空间、调度由用户控制)主线程是一个物理线程,直接作用在 cpu 上的。是重量级的,非常耗费 cpu 资源。协…...

ubuntu22.04@laptop OpenCV定制化安装
ubuntu22.04laptop OpenCV定制化安装 1. 源由2. 默认配置3. 定制配置4. 定制安装5. 定制OpenCV-4.9.05.1 修改opencv.conf5.2 加载so文件5.3 修改bash环境变量5.4 增加pkgconfig5.5 检查OpenCV-4.9.0安装 6. 总结7. 参考资料 1. 源由 目前,能Google到的代码层次不齐…...

linux系统非关系型数据库redis
redis 介绍redis的特点:缓存 安装安装单机版redisredis的相关工具 介绍 redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库 redis的官网:redis.ioredis的特点: 丰富的数据结构 支持持久化 支持事务 支持主从缓存 类型 …...

【LeetCode: 292. Nim 游戏+ 博弈问题】
🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...

Android 9.0 禁用adb reboot recovery命令实现正常重启功能
1.前言 在9.0的系统rom定制化开发中,在定制recovery模块的时候,由于产品开发需要要求禁用recovery的相关功能,比如在通过adb命令的 adb reboot recovery的方式进入recovery也需要实现禁用,所以就需要了解相关进入recovery流程来禁用该功能 2.禁用adb reboot recovery命…...

分析网站架构:浏览器插件
一、Wappalyzer 1.1 介绍 Wappalyzer 是一款用于识别网站所使用技术栈的浏览器插件。它能够分析正在浏览的网页,检测出网站所使用的各种技术和框架,如内容管理系统(CMS)、JavaScript库、Web服务器等。用户只需安装 Wappalyzer 插…...

CentOS7搭建Hadoop集群
准备工作 1、准备三台虚拟机,参考:CentOS7集群环境搭建(3台)-CSDN博客 2、配置虚拟机之间免密登录,参考:CentOS7集群配置免密登录-CSDN博客 3、虚拟机分别安装jdk,参考:CentOS7集…...

10.0 Zookeeper 权限控制 ACL
zookeeper 的 ACL(Access Control List,访问控制表)权限在生产环境是特别重要的,所以本章节特别介绍一下。 ACL 权限可以针对节点设置相关读写等权限,保障数据安全性。 permissions 可以指定不同的权限范围及角色。 …...

容器化技术基础概念:雪花服务器与凤凰服务器
雪花服务器与凤凰服务器:两种软件部署领域的基础设施对比 在软件部署领域,服务器管理在正常运行时间、效率和安全性方面发挥着关键作用。存在两种截然不同的方法:雪花服务器和凤凰服务器。了解它们之间的区别将帮助您选择最适合您需求的策略…...