C++ STL vector 模拟实现
✅<1>主页:我的代码爱吃辣
📃<2>知识讲解:C++之STL
🔥<3>创作者:我的代码爱吃辣
☂️<4>开发环境:Visual Studio 2022
💬<5>前言:上次我们已经数字会用了vector,这次我们对其底层更深一步挖掘,其中重点是,Vector中一些深浅拷贝问题。
目录
一.Vector模拟实现的整体框架
二. Vector的构造与析构
三.size(),capacity()
四.reserve(),resize()
1.reserve()
2.resize
五.push_back(),pop_back()
1.push_back()
2. pop_back()
六.Vector的迭代器
七.operator [ ]
八.insert(),erase()
1.迭代器失效
2.insert()
3.erase()
九.再看Vector构造函数
十.拷贝构造
1.深浅拷贝
2.正确的拷贝构造代码:
3.正确的 reserve()
4.赋值运算符重载
十一.总体代码
一.Vector模拟实现的整体框架
我们先认识一下Vector的整体模拟实现框架,Vector在功能上就是我们数据结构阶段实现的顺序表基本一致,但是Vector在成员框架上与顺序表有所不同,且Vector使用类和对象封装支持模板泛型。
template<class T>
class Vector
{
public://迭代器类型typedef T* iterator;typedef const T* const_iterator;//...private:iterator _start;//数据存储首地址iterator _finish;//有效数据尾部地址下一个地址。iterator _end_of_storage;//容量尾地址下一个地址
};
Vector的迭代器是对顺序表原生类型指针的封装。
二. Vector的构造与析构
Vector主要是对成员变量初始化:
Vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}
析构主要释放我们申请的空间:
~Vector(){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
三.size(),capacity()
size()返回当前顺序表存储的数据个数,capacity()返回当前顺序表的容量。
size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}

四.reserve(),resize()
1.reserve()
设置Vector的容量,注意容量支持增加,但是不支持减小。
void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){memcpy(tmp, _start, sizeof(T) * capacity()); /*error !!*/delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}
注意:
此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,然后计算的size也并非是,准确的size。除此之外这份代码依旧是有问题的。我们后面解释。
错误代码如下:
void reserve(size_t capa){if (capacity() < capa){iterator tmp = new T[capa];if (_start){memcpy(tmp, _start, sizeof(T) * capacity());delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + size();_end_of_storage = _start + capa;}}

2.resize
提供改变存储数据的个数的能力。如果 n < size 时就是删除数据,n > size且空间不够时需要扩容+初始化,空间足够,仅需要初始化剩下的空间。
void resize(size_t n, T val = T()){ //1.n < size;-->删除数据if (n < size()){_finish = _start + n;}//2.n > sizeelse {//(1)如果空间不足,需要扩容+初始化if (n >= capacity()){reserve(n);}//(2)空间足够,仅需要初始化剩下的空间while (_finish != _start + n){*(_finish) = val;_finish++;}}}
五.push_back(),pop_back()
1.push_back()
从尾部插入一个数据。
void push_back(const T& val){//检查是否需要扩容if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//插入数据*(_finish) = val;_finish++;}
2. pop_back()
bool empty() const {return size() == 0;}void pop_back(){//判空assert(!empty());//我们仅需将维护尾部数据的指针向前挪一位。_finish--;}
六.Vector的迭代器
typedef T* iterator;typedef const T* const_iterator;
Vector底层就是顺序存储的结构,所以可以使用原生指针作为迭代器。
//普通迭代器iterator begin(){return _start;}iterator end(){return _finish;}//const 迭代器const_iterator begin()const {return _start;}const_iterator end()const{return _finish;}
有了迭代器就可以支持迭代器访问,和范围for。
int main()
{Vector<int> v1;v1.push_back(100);v1.push_back(200);v1.push_back(300);v1.push_back(400);v1.push_back(500);v1.push_back(600);v1.push_back(700);Vector<int>::iterator it = v1.begin();while (it != v1.end()){cout << *it << " ";it++;}cout << endl;for (auto e : v1){cout << e << " ";}return 0;
}

七.operator [ ]
//穿引用返回T& operator[](size_t pos){//判断位置的合法性assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}

八.insert(),erase()
1.迭代器失效
在模拟实现之前我们先看一下什么是迭代器失效问题:
迭代器失效问题通常发生在容器类的成员函数中,例如erase和insert。在这些函数中,迭代器被重置或修改,导致原始迭代器不再指向容器的正确位置,从而导致迭代器失效。
int main()
{vector<int> v1;v1.push_back(100);v1.push_back(200);v1.push_back(300);v1.push_back(400);v1.push_back(500);v1.push_back(600);v1.push_back(700); vector<int>::iterator pos = find(v1.begin(), v1.end(),200);//对pos位置插入v1.insert(pos, 150);//pos已经失效v1.insert(pos, 170);return 0;
}

原理图:
情况一:

上述代码中的迭代器失效问题也是属于这种情况。
情况二:

2.insert()
iterator insert(iterator pos,T val){assert(pos >= _start);assert(pos < _finish);//迭代器失效问题,记录pos的相对位置int len = pos - _start;if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//扩容后重新计算pos,没有发生扩容pos不变pos = _start + len;iterator end = _finish;//数据挪动while (end >= pos){(*end) = *(end - 1);end--;}_finish++;(*pos) = val;return pos;}
在使用pos时要注意扩容会使得pos失效,需要重新计算pos位置。
3.erase()
iterator erase(iterator pos){//判断位置是否合法assert(pos >= _start);assert(pos < _finish);iterator end = pos ;/挪动数据删除while (end < _finish){*end = *(end + 1);end++;}_finish--;return pos;}
九.再看Vector构造函数
std中的vector还支持使用指定个数和初始化值初始化,和迭代器区间初始化。这两个功能在我们平时也是能用到的。
//1.Vector<T> v(5,10);创建一个Vector并且初始化前5个值为10Vector(size_t n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}//2.迭代器初始化,[frist,lest)template<class InputIterator>Vector(InputIterator frist, InputIterator lest):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(lest - frist);while (frist != lest){push_back(*frist);frist++;}}//3.防止构造函数调用错误Vector(int n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}
第三个构造函数的作用是防止构造调用错误冲突,在我们进行如下的调用时:
Vector<int> v2(5, 10);
编译器会以为我们在调用迭代器区间初始化构造函数,因为经过模板的推导,只有迭代器区间初始化构造函数,更适合这个调用。然后将一个整形当作地址在迭代器区间初始化构造函数里面解引用了,报错是:非法的间接寻址。

正常调用结果:
十.拷贝构造
今天这里编译器默认生成的拷贝构造显然是不能用了。
1.深浅拷贝
万万不可以直接使用拷贝函数按二进制或者按字节直接拷贝了。
错误代码1:
Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(v.capacity());//万万不可以直接按二进制拷贝memcpy(_start, v._start, sizeof(T) * v.capacity()); /*error!!!!*/_finish = _start + v.size();_end_of_storage = _start + v.capacity();}
原因:
调用处代码:
int main()
{string str("abcdefg");Vector<string> v2(5,str);Vector<string> v3(v2);return 0;
}

会使得我们同一块空间被delete两次从而引发内存错误。
2.正确的拷贝构造代码:
Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(v.capacity());//这里我们将数据一个一个push进去,这样我们借助push_back底层插入的时候,//会使用string的赋值构造,完成深拷贝。for (int i = 0; i < v.size(); i++){push_back(v[i]);}}//现代写法Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){Vector<T> tmp(v.begin(), v.end());swap(tmp);}
错误代码2:reserve()
void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){//这里千万不能按二进制直接拷贝.memcpy(tmp, _start, sizeof(T) * capacity()); /*error !!*/delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}
这里我们仍然是使用了memcpy。
调用处代码:
int main()
{string str("abcdefg");Vector<string> v2;for (int i = 0; i < 6; i++){v2.push_back(str);}return 0;
}

3.正确的 reserve()
void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){//这里千万不能按二进制直接拷贝.//memcpy(tmp, _start, sizeof(T) * capacity()); /*ror !!*/for (int i = 0; i < size(); i++){//=内置类型直接赋值,自定义类型使用赋值构造tmp[i]=_start[i];}delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}
这里有一个细节就是在reserve和拷贝构造的拷贝数据的时候我们都是使用了赋值。问题我们并没有重载赋值运算符,编译器自动生成,简单来说就是这里又会是一个浅拷贝。
4.赋值运算符重载
//传统写法Vector<T>& operator=(const Vector<T>& v){T* tmp = new T[v.capacity()];if (_start){for (int i = 0; i < v.size(); i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + v.size();_end_of_storage = _start + v.capacity();return *this;}//现代写法void swap(Vector<T>& v ){std::swap(v._start, _start);std::swap(v._finish, _finish);std::swap(v._end_of_storage, _end_of_storage);}Vector<T>& operator=(Vector<T> v){swap(v);return *this;}
现代写法利用,拷贝构造拷贝出来的对象,然后交换对象的成员。
十一.总体代码
#pragma once
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cassert>
using namespace std;template<class T>
class Vector
{
public:typedef T* iterator;typedef const T* const_iterator;Vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}//1.Vector<T> v(5,10);创建一个Vector并且初始化前5个值为10Vector(size_t n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}//2.迭代器初始化,[frist,lest)template<class InputIterator>Vector(InputIterator frist, InputIterator lest):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(lest - frist);while (frist != lest){push_back(*frist);frist++;}}//3.防止构造函数调用冲突Vector(int n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}//传统写法 拷贝构造//Vector(const Vector<T>& v)// :_start(nullptr),// _finish(nullptr),// _end_of_storage(nullptr)//{// reserve(v.capacity());// //这里我们将数据一个一个push进去,这样我们借助push_back底层插入的时候,// //会使用string的赋值构造,完成深拷贝。// for (int i = 0; i < v.size(); i++)// {// _start[i] = v[i];// }//}//现代写法,拷贝构造Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){Vector<T> tmp(v.begin(), v.end());swap(tmp);}//传统写法,赋值拷贝//Vector<T>& operator=(const Vector<T>& v)//{// T* tmp = new T[v.capacity()];// if (_start)// {// for (int i = 0; i < v.size(); i++)// {// tmp[i] = _start[i];// }// delete[] _start;// }// _start = tmp;// _finish = _start + v.size();// _end_of_storage = _start + v.capacity();// // return *this;//}void swap(Vector<T>& v ){std::swap(v._start, _start);std::swap(v._finish, _finish);std::swap(v._end_of_storage, _end_of_storage);}//现代写法,赋值拷贝Vector<T>& operator=(Vector<T> v){swap(v);return *this;}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){//这里千万不能按二进制直接拷贝.//memcpy(tmp, _start, sizeof(T) * capacity()); /*ror !!*/for (int i = 0; i < size(); i++){//=内置类型直接赋值,自定义类型使用赋值构造tmp[i]=_start[i];}delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}void resize(size_t n, T val = T()){ //1.n < size;-->删除数据if (n < size()){_finish = _start + n;}//2.n > sizeelse {//(1)如果空间不足,需要扩容+初始化if (n >= capacity()){reserve(n);}//(2)空间足够,仅需要初始化剩下的空间while (_finish != _start + n){*(_finish) = val;_finish++;}}}//穿引用返回T& operator[](size_t pos){//判断位置的合法性assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}void push_back(const T& val){if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//内置类型直接赋值,自定义类型使用赋值构造*(_finish) = val;_finish++;}bool empty() const {return size() == 0;}void pop_back(){//判空assert(!empty());//我们仅需将维护尾部数据的指针向前挪一位。_finish--;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator end = pos ;while (end < _finish){*end = *(end + 1);end++;}_finish--;return pos;}iterator insert(iterator pos,T val){assert(pos >= _start);assert(pos < _finish);//迭代器失效问题,记录pos的相对位置int len = pos - _start;if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//扩容后重新计算pos,没有发生扩容pos不变pos = _start + len;iterator end = _finish;//数据挪动while (end >= pos){(*end) = *(end - 1);end--;}_finish++;(*pos) = val;return pos;}//普通迭代器iterator begin(){return _start;}iterator end(){return _finish;}//const 迭代器const_iterator begin()const {return _start;}const_iterator end()const{return _finish;}~Vector(){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
private:iterator _start;//数据存储首地址iterator _finish;//有效数据尾部地址下一个地址。iterator _end_of_storage;//容量尾地址下一个地址int tmp;
};

相关文章:
C++ STL vector 模拟实现
✅<1>主页:我的代码爱吃辣 📃<2>知识讲解:C之STL 🔥<3>创作者:我的代码爱吃辣 ☂️<4>开发环境:Visual Studio 2022 💬<5>前言:上次我们已经数字会用…...
51单片机学习--红外遥控(外部中断)
需要利用下面这个红外接收头,OUT口会发出红外信号对应的高低电平,由于发送的速度很快,所以需要把OUT引脚接在外部中断引脚上,当OUT一旦产生下降沿,马上进中断,这样响应会更及时。 外部中断引脚位于P3_2和P…...
后端开发10.规格模块
概述 简介 效果图...
腾讯出了一个新聊天软件M8
众所周知,如今国内互联网,微信和QQ无疑是社交领域的霸主。 下载:https://www.123pan.com/s/BP5A-RW4xh.html 不过,它们也有各自局限性,比如难以结识新朋友、功能过于复杂等。 这让用户产生厌倦,再加上近几年AI、元宇…...
C++ QT(一)
目录 初识QtQt 是什么Qt 能做什么Qt/C与QML 如何选择Qt 版本Windows 下安装QtLinux 下安装Qt安装Qt配置Qt Creator 输入中文配置Ubuntu 中文环境配置中文输入法 Qt Creator 简单使用Qt Creator 界面组成Qt Creator 设置 第一个Qt 程序新建一个项目项目文件介绍项目文件*.pro样式…...
微信小程序时钟
微信小程序自定义时钟,模拟翻牌时钟。1、页面布局 <view class"date-time-box"><view class"date-box">{{nowDate}}</view><view class"time-box"><view><image class"pic01 {{move[0]?move…...
HttpRunner自动化工具之设置代理和请求证书验证
httprunner设置代理: httprunner 库本身没有提供设置代理的接口,但是底层使用了urllib.requests 等库,可以设置HTTP_PROXY 和HTTPS_PROXY 环境变量,常用的网络库会自动识别这些环境变量。 日常调试使用代理(如charles…...
opsForHash() 与 opsForValue 请问有什么区别?
👉:🔗官方API参考手册 如图,opsForHash()返回HashOperations<K,HK,HV>但是 opsForValue()返回ValueOperations<K,V>… 区别就是opsForHash的返回值泛型中有K,HK,HV,其中K是Redis指定的某个数据库里面某一个关键字(由…...
具有吸引子的非线性系统(MatlabSimulink实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Linux一些常见的命令
1. 基础命令 1. ls: 列出目录内容。- 例如:ls -l 以长格式列出文件和目录。2. cd: 切换工作目录。- 例如:cd /home/user 进入 /home/user 目录。3. pwd: 显示当前工作目录的路径。4. mkdir: 创建新目录。-…...
正则表达式的基本知识
正则表达式是一种用于匹配和操作字符串的强大工具。它是由一系列字符和特殊符号组成的模式,可以用来检查字符串是否符合某种模式,进行匹配、替换、提取等操作。 下面是一些常见的正则表达式元字符和语法: 1. 字符匹配: - 普通…...
如何⽤webpack 来优化前端性能
如何⽤webpack 来优化前端性能? ⽤webpack 优化前端性能是指优化 webpack 的输出结果,让打包的最终结果在浏览器运⾏快速⾼效。 压缩代码:删除多余的代码、注释、简化代码的写法等等⽅式。可以利⽤webpack的 UglifyJsPlugin 和 ParallelUgl…...
人机交互中的混合多重反馈
人机交互中态、势、感、知的混合多重反馈是指在交互过程中综合运用不同方面的反馈信息,包括用户态度(态)、行为动势(势)、情感体验(感)和认知反馈(知)。这种多重反馈可以…...
CSS:服务器字体 与 响应式布局(用法 + 例子 + 效果)
文章目录 服务器字体定义 服务器字体使用例子 响应式布局设备类型设备特性例子 服务器字体 解决字体不一致而产生的。 首先,在网上把字体下载好。 定义 服务器字体 font-face{font-family:字体名称;src:url(字体资源路径); }使用 在需要使用的选择器里加上 font…...
24届近3年上海电力大学自动化考研院校分析
今天给大家带来的是上海电力大学控制考研分析 满满干货~还不快快点赞收藏 一、上海电力大学 学校简介 上海电力大学(Shanghai University of Electric Power),位于上海市,是中央与上海市共建、以上海市管理为主的全日…...
PostgreSQL查询慢sql原因和优化方案
PostgreSQL sql查询慢优化方案有一下几种解决方案: 1.关闭会话 查询慢sql的执行会话,关闭进程。 查看数据库后台连接进程 SELECT count(*) FROM pg_stat_activity;SELECT * FROM pg_stat_activity; 查看数据库后台连接进程,但是此条SQL不…...
Leetcode 21. 合并两个有序链表
题目描述 题目链接:https://leetcode.cn/problems/merge-two-sorted-lists/description/ 思路 两个链表都是升序链表,新建一个链表,引入伪头节点作为辅助节点,将各节点添加到伪节点之后,再用一个cur节点指向新链表的…...
[tool] Ubuntu 设置开机启动python脚本
前言: 话说我每次设置的服务器,再次开机,ip都会随之改变,固定ip有时候确定不好用。所以为啥不让让每次启动都发送ip给我呢。 步骤: sudo touch /etc/rc.local sudo chmod 777 /etc/rc.local sudo systemctl enable rc-local.se…...
「何」到底该读「なん」还是「なに」?柯桥学日语
「何」到底该读「なん」还是「なに」? 首先,讲一个规律,大家记住就行。当「何」后面所接单词的第一个发音在“た”、“だ”、“な”行时,读作“なん”。一般这种情况下,后面跟的是の、でも、です和だ。 用例ÿ…...
github - 创建组织-Team
地址记录: github创建组织(organization) - 知乎...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
