STL之list容器的介绍与模拟实现+适配器

STL之list容器的介绍与模拟实现+适配器

  • 1. list的介绍
  • 2. list容器的使用
    • 2.1 list的定义
    • 2.2 list iterator的使用
    • 2.3 list capacity
    • 2.4 list element access
    • 2.5 list modifiers
    • 2.6 list的迭代器失效
  • 3. list的模拟实现
    • 3.1 架构搭建
    • 3.2 迭代器
      • 3.2.1 正向迭代器
      • 3.2.2反向迭代器+适配器
    • 3.3 空间控制模块
    • 3.4 数据的访问
    • 3.5 增加/删除数据
    • 3.6 构造/拷贝构造/析构
  • 4. 整体代码逻辑

所属专栏:C“嘎嘎" 系统学习❤️
🚀 >博主首页:初阳785❤️
🚀 >代码托管:chuyang785❤️
🚀 >感谢大家的支持,您的点赞和关注是对我最大的支持!!!❤️
🚀 >博主也会更加的努力,创作出更优质的博文!!❤️

1. list的介绍

list的文档介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向
    其前一个元素和后一个元素。
  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list
    的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间
    开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这
    可能是一个重要的因素)

2. list容器的使用

2.1 list的定义

构造函数( (constructor))接口说明
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list

2.2 list iterator的使用

函数声明接口说明
begin +end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin +rend返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置

【注意】

  1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
  2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

2.3 list capacity

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

2.4 list element access

函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

2.5 list modifiers

函数声明接口说明
push_front在list首元素前插入值为val的元素
pop_front删除list中第一个元素
push_back在list尾部插入值为val的元素
pop_back删除list中最后一个元素
insert在list position 位置中插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素

2.6 list的迭代器失效

前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节
点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代
器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

void TestListIterator1()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while (it != l.end()){// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值l.erase(it);++it;}
}// 改正
void TestListIterator()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while (it != l.end()){it = l.erase(it);}
}

3. list的模拟实现

3.1 架构搭建

  • 首先,list容器的底层实现是一个双向循环链表。所以在实现本章节的前提下,我们首先要熟知我们学习C语言的时候是怎么实现一个带头双向循环链表的。核心的逻辑思维是一模一样的。
  • 所以在实现的前提下,我们可以先从C语言数据结构着手起步。
  • 整体的构架就是:1. 要有一个节点的类,里面包含了两个指针next和prev,和一个存放数据的变量val。2. 就是构建list类,在类里面进行一些类的操作。
  template<class T>struct ListNode{ListNode(const T& val = T()):_pPre(nullptr),_pNext(nullptr),_val(val){}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val;};//list类template<class T>class list{typedef ListNode<T> Node;//typedef Node* PNode;public://正向迭代器typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;//反向迭代器typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;typedef Reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;public:///// List的构造list(){……}//拷贝构造list(const list<T>& l){……}~list(){……}
//………………private://创建新节点void CreateHead(){_pHead = new Node;_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;}Node* _pHead;
}

3.2 迭代器

同样提供两个版本const 和 非const版本的。

  • 但是这里要注意一点就是,我们的迭代器在进行移动的时候无非就是++/–操作,但是我们可由直接进行(iterator)a++吗?如果这是个内置类型的话那自然是可以的。但是组成list容器并非是内置类型,而是自定义类型,每个节点都是一个类,而自定义类型是无法直接进行++/–等一些类的运算符操作的。要想对自定义类型进行运算符操作就必须要使用运算符重载函数。所以为了可以对迭代器进行运算符操作就也要定义一个迭代器类,并在类中包含要进行操作的自定义类型的对象,在类中进行运算符操作。

3.2.1 正向迭代器

template<class T, class Ref, class Ptr>
class ListIterator
{
public:typedef ListNode<T> PNode;typedef ListIterator<T, Ref, Ptr> Self; PNode* _pNode;ListIterator(PNode* pNode = nullptr):_pNode(pNode){}//ListIterator(const Self& l);Ref operator*(){return _pNode->_val;}Ptr operator->(){return &_pNode->_val;}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self tmp(*this);//拷贝构造(浅拷贝)_pNode = _pNode->_pNext;return tmp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self& operator--(int){Self tmp(*this);_pNode = _pNode->_pPre;return tmp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return _pNode == l._pNode;}
};
  • 注:这里我们可以看到我们创建ListIterator类的时候使用了类模板,并且看到模板参数中不止一个参数,而是多了两个Ref和Ptr。至于理由是:我们要实现两个版本的迭代器,一个是const和非const版本的,而这两个版本的区别无非就是返回的引用值是否能被修改该,也就是重载*解引用的时候,const版本返回的是const T&,非const版本返回的就是T&,除了这个其他的地方都一样。那如果想两个都实现是不是就要copy一份呢?一下写两个出来呢?所以这个时候模板参数起作用了。定义Ref模板参数,不管是T&还是const T&我们都返回Ref只是当我们想调用const版本的时候就给模板传const T&的类型,我们想调用非cosnt版本的时候就传T&类型的给模板就行,这样就可以避免代码重复问题,提高复用度。
  • 同样的既然我们定义是一个自定义类型的节点,迭代器可以看作是一个指针,有了自定义类型和之指针我们就可以通过指针+ (->)的方式拿到节点里面的val值,但是->同样也有两个版本,所以做法和上面的是一样的,只需要多个模板参数传一个值Ptr就行,具体调用什么版本的就传什么类型的过去就行。
  • 这里同样也要注意的一点就是重载(->)的时候,我们是这样定义的:
    Ptr operator->() { return &_pNode->_val; }
    本来我们使用的时候应该it->->val这样有两个箭头使用,也就是it.operator->()->val这样去使用的,但是编译为了方便使用做了优化只需要一个箭头就可进行访问了。

3.2.2反向迭代器+适配器

适配器:
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总
结),该种模式是将一个类的接口转换成客户希望的另外一个接口
在这里插入图片描述
用简单话来概括适配器就是——用现有的东西适配出一个新的东西。

我们的反向迭代器其实就是用到了适配器的概念,用到就是正向迭代器适配出来的。
在这里插入图片描述

  • rbegin的++就是end的–,rend的–就是begin的++,要取rbegin指向的值就是–end()在解引用得到。
    所以我们只需要讲正向迭代器的类型当作参数传个我们的反向迭代器的类模板参数就行。
  • 同样的这里也需要传三个模板参数,原理和上面是一样的。
template<class Iterator, class Pef, class Ptr>
struct Reverse_iterator
{typedef Reverse_iterator<Iterator, Pef, Ptr> self;Iterator cur;Reverse_iterator(Iterator it):cur(it){}Ptr operator->(){return &(operator*());}self& operator++(){--cur;return *this;}self operator++(int){Iterator tmp = cur;--cur;return tmp;}self& operator--(){++cur;return *this;}self operator--(int){Iterator tmp = cur;++cur;return tmp;}Pef operator*(){Iterator tmp = cur;--tmp;return *tmp;}bool operator!=(const self& l){return cur != l.cur;}bool operator==(const self& l){return cur == l.cur;}
};

3.3 空间控制模块

  1. 返回list容器的大小
size_t size() const
{size_t len = 0;const_iterator it = begin();//这里要用const_iterator迭代器,this被const修饰了while (it != end()){++len;++it;}return len;
}
  1. list容器是否为空
bool empty()const
{return begin() == end();
}
  1. resize
void resize(size_t newsize, const T& data = T())
{size_t oldsize = size();if (newsize <= oldsize){// 有效元素个数减少到newsizewhile (newsize < oldsize){pop_back();oldsize--;}}else{while (oldsize < newsize){push_back(data);oldsize++;}}
}

3.4 数据的访问

  1. 访问头节点的值
T& front()
{return (begin()._pNode)->_pNext->_val;
}
const T& front()const
{return (begin()._pNode)->_pNext->_val;}
  1. 访问尾节点的值
T& back()
{return (end()._pNode)->_pPre->_val;
}
const T& back()const
{return (end()._pNode)->_pPre->_val;
}

3.5 增加/删除数据

  1. 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T& val)
{Node* newnode = new Node;newnode->_val = val;Node* cur = pos._pNode;Node* prev = cur->_pPre;prev->_pNext = newnode;newnode->_pPre = prev;newnode->_pNext = cur;cur->_pPre = newnode;return iterator(newnode);
}
  1. 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos){Node* cur = pos._pNode;Node* prev = cur->_pPre;Node* next = cur->_pNext;prev->_pNext = next;next->_pPre = prev;return iterator(next);}
  1. push_back/pop_back
void push_back(const T& val) 
{insert(end(), val); 
}
void pop_back() 
{ erase(--end()); 
}
  1. push_front/pop_front
void push_front(const T& val) 
{ insert(begin(), val); 
}
void pop_front() 
{ erase(begin()); 
}
  1. 清除数据
void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}

3.6 构造/拷贝构造/析构

  1. 构造函数
list()
{CreateHead();
}list(int n, const T& value = T())
{CreateHead();//先创建哨兵位for (int i = 0; i < n; i++){push_back(value);}
}template <class Iterator>
list(Iterator first, Iterator last)
{CreateHead();//先创建哨兵位while (first != last){push_back(*first);++first;}
}
  1. 拷贝构造
list(const list<T>& l)
{CreateHead();//先创建哨兵位const_iterator it = l.begin();while (it != l.end()){push_back(*it);++it;}
}
  1. 赋值运算符重载
void swap(list<T>& l)
{std::swap(_pHead, l._pHead);
}list<T>& operator=(list<T> l) 
{swap(l);return *this;
}
  1. 析构
~list()
{//iterator it = begin();//while (it != end())//{//    Node* cur = it._pNode;//    _pHead->_pNext = cur->_pNext;//    delete cur;//}clear();delete _pHead;_pHead = nullptr;
}

4. 整体代码逻辑

namespace qfw
{// List的节点类template<class T>struct ListNode{ListNode(const T& val = T()):_pPre(nullptr),_pNext(nullptr),_val(val){}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val;};//List的迭代器类template<class T, class Ref, class Ptr>class ListIterator{public:typedef ListNode<T> PNode;typedef ListIterator<T, Ref, Ptr> Self; PNode* _pNode;ListIterator(PNode* pNode = nullptr):_pNode(pNode){}//ListIterator(const Self& l);Ref operator*(){return _pNode->_val;}Ptr operator->(){return &_pNode->_val;}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self tmp(*this);//拷贝构造(浅拷贝)_pNode = _pNode->_pNext;return tmp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self& operator--(int){Self tmp(*this);_pNode = _pNode->_pPre;return tmp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return _pNode == l._pNode;}};template<class Iterator, class Pef, class Ptr>struct Reverse_iterator{typedef Reverse_iterator<Iterator, Pef, Ptr> self;Iterator cur;Reverse_iterator(Iterator it):cur(it){}Ptr operator->(){return &(operator*());}self& operator++(){--cur;return *this;}self operator++(int){Iterator tmp = cur;--cur;return tmp;}self& operator--(){++cur;return *this;}self operator--(int){Iterator tmp = cur;++cur;return tmp;}Pef operator*(){Iterator tmp = cur;--tmp;return *tmp;}bool operator!=(const self& l){return cur != l.cur;}bool operator==(const self& l){return cur == l.cur;}};//list类template<class T>class list{typedef ListNode<T> Node;//typedef Node* PNode;public://正向迭代器typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;//反向迭代器typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;typedef Reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;public:///// List的构造list(){CreateHead();}list(int n, const T& value = T()){CreateHead();//先创建哨兵位for (int i = 0; i < n; i++){push_back(value);}}template <class Iterator>list(Iterator first, Iterator last){CreateHead();//先创建哨兵位while (first != last){push_back(*first);++first;}}//拷贝构造list(const list<T>& l){CreateHead();//先创建哨兵位const_iterator it = l.begin();while (it != l.end()){push_back(*it);++it;}}void swap(list<T>& l){std::swap(_pHead, l._pHead);}list<T>& operator=(list<T> l) {swap(l);return *this;}~list(){//iterator it = begin();//while (it != end())//{//    Node* cur = it._pNode;//    _pHead->_pNext = cur->_pNext;//    delete cur;//}clear();delete _pHead;_pHead = nullptr;}///// List Iteratoriterator begin(){return iterator(_pHead->_pNext);}iterator end(){return iterator(_pHead);}const_iterator begin() const{return const_iterator(_pHead->_pNext);}const_iterator end() const{return const_iterator(_pHead);}reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}///// List Capacitysize_t size() const{size_t len = 0;const_iterator it = begin();//这里要用const_iterator迭代器,this被const修饰了while (it != end()){++len;++it;}return len;}bool empty()const{return begin() == end();}void resize(size_t newsize, const T& data = T()){size_t oldsize = size();if (newsize <= oldsize){// 有效元素个数减少到newsizewhile (newsize < oldsize){pop_back();oldsize--;}}else{while (oldsize < newsize){push_back(data);oldsize++;}}}// List AccessT& front(){return (begin()._pNode)->_pNext->_val;}const T& front()const{return (begin()._pNode)->_pNext->_val;}T& back(){return (end()._pNode)->_pPre->_val;}const T& back()const{return (end()._pNode)->_pPre->_val;}// List Modifyvoid push_back(const T& val) {insert(end(), val); }void pop_back() { erase(--end()); }void push_front(const T& val) { insert(begin(), val); }void pop_front() { erase(begin()); }// 在pos位置前插入值为val的节点iterator insert(iterator pos, const T& val){Node* newnode = new Node;newnode->_val = val;Node* cur = pos._pNode;Node* prev = cur->_pPre;prev->_pNext = newnode;newnode->_pPre = prev;newnode->_pNext = cur;cur->_pPre = newnode;return iterator(newnode);}// 删除pos位置的节点,返回该节点的下一个位置iterator erase(iterator pos){Node* cur = pos._pNode;Node* prev = cur->_pPre;Node* next = cur->_pNext;prev->_pNext = next;next->_pPre = prev;return iterator(next);}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}private:void CreateHead(){_pHead = new Node;_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;}Node* _pHead;};

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

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

相关文章

大脑是宇宙中最复杂的物体——科学家们试图破译它,读懂人们的思想

2023年&#xff0c;德克萨斯大学HuthLab进行的一项研究在神经科学和技术领域引发了震动。通过人工智能(AI)和脑成像技术的结合&#xff0c;无法与外界交流的人的思想首次被翻译成连续的自然语言。 这是迄今为止最接近读心术的科学方法。在过去的二十年里&#xff0c;神经成像技…

Qt中程序发布及常见问题

1、引言 当我们写好一个程序时通常需要发布给用户使用&#xff0c;那么在Qt中程序又是如何实现发布的呢&#xff0c;这里我就来浅谈一下qt中如何发布程序&#xff0c;以及发布程序时的常见问题。 2、发布过程 2.1、切换为release模式 当我们写qt程序时默认是debug模式&#x…

【51单片机】添加模块代码的常见问题(图示&代码演示)

前言 大家好吖&#xff0c;欢迎来到 YY 滴 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 本章节是Lcd1602章节的一部分&#xff0c;以把4个Lcd驱动程序添加为例子&#xff0c;完整传送门在下方传送门 欢迎订阅 YY滴C专栏&…

Red Panda Dev C++ Maker【2.0自创黑客版】使用说明

https://download.csdn.net/download/HappyStarLap/88825258https://download.csdn.net/download/HappyStarLap/88825258Red Panda Dev C&#xff08;旧名 Dev-C 2000&#xff09;是 Orwell Dev-C 的改进分支。包括heker.h、Heike.h、easxy.h 和Art_Text.h。Orwell Dev-C 自 20…

(三)elasticsearch 源码之启动流程分析

https://www.cnblogs.com/darcy-yuan/p/17007635.html 1.前面我们在《&#xff08;一&#xff09;elasticsearch 编译和启动》和 《&#xff08;二&#xff09;elasticsearch 源码目录 》简单了解下es&#xff08;elasticsearch&#xff0c;下同&#xff09;&#xff0c;现在我…

基于tomcat运行jenkins常见的报错处理

目录 1.jenkins.util.SystemProperties$Listener错误 升级jdk11可能遇到的坑 2.java.lang.RuntimeException: Fontconfig head is null, check your fonts or fonts configuration 3.There were errors checking the update sites: UnknownHostException:updates.jenkins.i…

Apache Zeppelin 整合 Spark 和 Hudi

一 环境信息 1.1 组件版本 组件版本Spark3.2.3Hudi0.14.0Zeppelin0.11.0-SNAPSHOT 1.2 环境准备 Zeppelin 整合 Spark 参考&#xff1a;Apache Zeppelin 一文打尽Hudi0.14.0编译参考&#xff1a;Hudi0.14.0 最新编译 二 整合 Spark 和 Hudi 2.1 配置 %spark.confSPARK_H…

Netty应用(三) 之 NIO开发使用 网络编程 多路复用

目录 重要&#xff1a;logback日志的引入以及整合步骤 5.NIO的开发使用 5.1 文件操作 5.1.1 读取文件内容 5.1.2 写入文件内容 5.1.3 文件的复制 5.2 网络编程 5.2.1 accept&#xff0c;read阻塞的NIO编程 5.2.2 把accept&#xff0c;read设置成非阻塞的NIO编程 5.2.3…

低代码平台与BPM:两者是否具有可比性?

传统上&#xff0c;业务流程管理 (BPM) 系统通过消除手动重复工作来帮助企业简化复杂的流程。它用于自动化、监控和分析业务流程&#xff0c;使高层管理人员的工作更轻松。这反过来又提高了所有其他相关利益相关者的生产力&#xff0c;并为业务增长铺平了道路。BPM 软件还使决策…

springboot174基于springboot的疾病防控综合系统的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

Linux-3 进程概念(三)

1.环境变量 1.1基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪里&#xff0c;但是照样可以链接成功…

初识Solidworks:我的第一份作业的感想

从来没用CAD软件画过机械设计图。但我脑子里有一种概念&#xff0c;无非就是把尺规作图软件化&#xff0c;更方便画图、更方便修改、更方便打印一些。但第一份 Solidworks 作业就颠覆了我的认知&#xff0c;考虑到这个软件的上市时间&#xff0c;让我意识到自己对 CAD 软件的认…

如何让内网client通过公网地址访问内网server?

第一步&#xff0c;实现任意公网用户访问内网server。按教育网规矩&#xff0c;公网过来的流量要访问校内网的server必须从教育专线&#xff08;路由器接口G0/0/1)进入。 第二步&#xff0c;实现内网主机通过公网地址210.43.2.3能够访问内网server192.168.1.2&#xff0c;图中①…

ES实战-book笔记1

#索引一个文档,-XPUT手动创建索引, curl -XPUT localhost:9200/get-together/_doc/1?pretty -H Content-Type: application/json -d {"name": "Elasticsearch Denver","organizer": "Lee" } #返回结果 {"_index" : "g…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Toggle组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Toggle组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Toggle组件 组件提供勾选框样式、状态按钮样式及开关样式。 子组件 仅当Toggl…

林浩然与杨凌芸的Java异常处理大冒险

林浩然与杨凌芸的Java异常处理大冒险 Lin Haoran and Yang Lingyun’s Java Exception Handling Adventure 在一个阳光明媚的午后&#xff0c;编程世界的英雄——林浩然和杨凌芸坐在Java王国的咖啡馆里&#xff0c;一边品尝着香醇的代码咖啡&#xff0c;一边探讨着他们的最新挑…

干掉Xshell,这款开源的终端工具逼格真高!

GitHub 上已经有 53.7k 的 star 了&#xff0c;这说明 Tabby 非常的受欢迎&#xff1a; https://github.com/eugeny/tabby Tabby 是一个高度可定制化的 跨平台的终端工具&#xff0c;支持 Windows、macOS 和 Linux&#xff0c;自带 SFTP 功能&#xff0c;能与 Linux 服务器轻…

Blazor入门100天 : 自做一个支持长按事件的按钮组件

好长时间没继续写这个系列博客了, 不知道大家还记得我吗? 话不多说,直接开撸. 配套源码 demo https://blazor.app1.es/b19LongPressButton ####1. 新建 net8 blazor 工程 b19LongPressButton 至于用什么模式大家各取所需, 我创建的是ssr单工程, 如果大家不小心建立错了按页…

Mysql——更新数据

注&#xff1a;文章参考&#xff1a; MySQL 更新数据 不同条件(批量)更新不同值_update批量更新同一列不同值-CSDN博客文章浏览阅读2w次&#xff0c;点赞20次&#xff0c;收藏70次。一般在更新时会遇到以下场景&#xff1a;1.全部更新&#xff1b;2.根据条件更新字段中的某部分…

[office] excel求乘积的公式和方法 #媒体#笔记#经验分享

excel求乘积的公式和方法 本文首先给出两个常规的excel求乘积的链接&#xff0c;然后再例举了一个文字和数字在同一单元格里面的excel求乘积的公式写法。 excel求乘积的方法分为两种&#xff0c;第一种是直接用四则运算的*来求乘积&#xff0c;另外一种就是使用PRODUCT乘积函数…