数据结构 ArrayList与顺序表

本节目标:

  1. 了解线性表和顺序表
  2. 能够实现简单的顺序表及其基本操作
  3. 认识 ArrayList类并且知道如何去使用

本篇文章正式进入数据结构!进入之前,先了解一下什么是线性表和顺序表

1.线性表与顺序表

线性表

线性表( linear list ) 是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列...

它在逻辑上是线性结构,也就是说是连续的一条直线,但是在物理结构上不一定是连续的。线性表在物理上储存时,通常以数组和链式结构的形式储存,例如:

顺序表

顺序表是用一段物理地址连续的存储单元异常储存数据元素的线性结构,一般情况下采用数组储存。在数组上完成数据的增删查改操作。

 2.实现简单的顺序表及其基本操作

在进入 ArrayList 前,我们可以先自己实现一个简单的顺序表和它的一些基本功能,在这个过程中,要特别记住,数据结构的两个特点:

  • 抽象,要有大概的结构
  • 逻辑非常的严谨

我们实现这个顺序表的过程中,会围绕着这两点展开!

这是我们要实现的顺序表和它的一些操作:

public class SeqList {    

        private int[] array;    

        private int size;    

        // 默认构造方法    

        SeqList(){   }    

        // 将顺序表的底层容量设置为initcapacity    

        SeqList(int initcapacity){   }

        // 新增元素,默认在数组最后新增

        public void add(int data) { }

        // 在 pos 位置新增元素

        public void add(int pos, int data) { }

        // 判定是否包含某个元素

        public boolean contains(int toFind) { return true; }

        // 查找某个元素对应的位置

        public int indexOf(int toFind) { return -1; }

        // 获取 pos 位置的元素

        public int get(int pos) { return -1; }

        // 给 pos 位置的元素设为 value

        public void set(int pos, int value) {   }

        //删除第一次出现的元素

        public void remove(int toRemove) {   }

        // 获取顺序表长度

        public int size() { return 0; }

        // 清空顺序表

        public void clear() {   }

        // 打印顺序表

        public void display() {   }

        //这个方法是为了看测试结果才弄的!

}

从易到难,一个一个弄。

构造方法

两个构造方法比较简单:

public SeqList() {}public SeqList(int initcapacity) {this.array = new int[initcapacity];}

接着轮到打印顺序表的方法

打印顺序表

对于打印操作,我们的思路:对存放数据的数组进行遍历,一个个打印,至于数组中存放有多少个数据,用 size 来表示数组中储存的元素个数。

public void display() {for (int i = 0; i < this.size; i++) {System.out.println(array[i] + " ");}}

接着轮到获取顺序表长度的方法

获取顺序表长度

//获取顺序表长度public int getSize() {return this.size;}

接着轮到判定顺序表是否包含某个元素的方法

 判定顺序表是否包含某个元素

要求:判断顺序表是否包含某个元素,若包含则返还true,否则返回false。

思路:可以对顺序表进行遍历,如果在遍历的过程中找到了这个元素,那么返回true,如果遍历结束了,还没有找到这个元素,就说明顺序表中不包含这个元素,那么返回false。

//判断顺序表是否包含某个元素,若包含则返还true,否则返回false。public boolean contains(int toFind) {for (int i = 0; i < this.size; i++) {if (array[i] == toFind) {return true;}}return false;}

接着轮到查找某个元素在顺序表的位置的方法

查找某个元素在顺序表的位置

要求:查找某个元素在顺序表的位置,如果顺序表包含这个元素,就返回它在顺序表中对应的位置,否则返回-1。

思路:思路与判定顺序表是否包含某个元素差不多。

//查找某个元素在顺序表的位置,如果顺序表包含这个元素,就返回它在顺序表中对应的位置,否则返回-1;public int indexOf(int toFind) {for (int i = 0; i < this.size; i++) {if (array[i] == toFind) {return i;}}return -1;}

简单的弄完了,现在稍微上上强度了,需要考虑的事变多了。

新增元素1

要求:新增元素,在数组最后的位置新增。

思路:我们前面说过,使用size来记录数组中储存的元素个数,那么size相当于是现在数组的最后一个位置,那么直接将要新增的元素放到 array[size],接着size++就好了。

 思路很清晰,但是现在有一个问题,如果数组满了怎么办?这样子可放不进去了,那么我们需要在新增的元素之前,判断一下数组是否满了,如果满了,需要对它进行扩容,这样才能放入新的元素。

//新增元素1public void add(int data) {if (isFull()) {scalingArray();}this.array[size] = data;this.size++;}//判断数组是否满了private boolean isFull() {return this.size == array.length;}//对数组进行扩容private void scalingArray() {this.array = Arrays.copyOf(this.array,2*this.array.length);//扩容为原来的两倍}

这里判断数组是否满了的方法与扩容数组的方法被private修饰,原因是我们不希望通过外部去直接访问它们,在内部使用即可!

在编写新增元素1的方法的过程中,发现:虽然看起来并不难实现,但是存在着许多的小细节,一不小心就会忽视,这就与之前说的数据结构的两个特点对应上了!

新增元素2

要求:新增元素,在指定位置新增元素。

思路:假设指定位置是pos,要在pos位置新增元素,那么只需要将pos及其后面的元素向后移动即可,把pos位置腾出空间,同时进行判满和是否扩容,最后size++即可。

看起来没啥问题合理,但是我们忽略了一件重要的是:pos一定合法吗?如果pos < 0 或者 pos > size(顺序表不允许脱节),那我们不就炸了!因此还得处理pos合不合法的问题。 对应它不合法的情况,我们可以自定义一个异常类,当pos不合法时抛出异常,接着通过try-catch处理即可。

自定义异常类(pos不合法):

public class PosIllegal extends RuntimeException{public PosIllegal() {}public PosIllegal(String str) {super(str);}
}

 新增元素2的方法:

//新增元素2public void add(int pos,int data) {try {isIllegal(pos);if (isFull()) {scalingArray();}for (int i = this.size - 1; i >= pos; i--) {array[i + 1] = array[i];}this.array[pos] = data;this.size++;}catch (PosIllegal e) {e.printStackTrace();}}//判断pos是否合法private void isIllegal(int pos) {if(pos < 0 || pos > this.size) {throw new PosIllegal("pos不合法!");}}
}

获取某个位置的元素

要求:通过传入下标pos获取pos位置的元素。

思路:和之前一样,在获取数据之前要对pos进行判断是否合法,但是这次判断pos合不合法与之前的不同,因为我们不能获取pos = size位置的元素,因此pos的范围只能是 pos >= 0 || pos < size。不仅如此,在判断pos是否合法前,我们还要判断数组是不是空的,如果是空的,那没办法获取元素。可以写一个自定义异常类来解决数组为空的情况,当数组为空时,抛出异常;如果数组不为空,则返回要获取的元素,若数组中没有该元素,则返回-1.

自定义异常类(数组为空):

public class EmptyException extends RuntimeException{public EmptyException() {}public EmptyException(String str) {super(str);}
}

获取某个位置元素的方法:

public int get(int pos) {try {isEmpty();isIllegal_1(pos);return this.array[pos];}catch (PosIllegal e) {e.printStackTrace();}catch (EmptyException e) {e.printStackTrace();}return -1;}//判断数组是否为空private void isEmpty() {if(this.size == 0) {throw new EmptyException("数组为空!");}}//判断pos是否合法——新的标准private void isIllegal_1(int pos) {if (pos < 0 || pos >= this.size) {throw new PosIllegal("pos不合法! ");}}

将某个位置的元素进行更改

要求:将下标为pos的元素进行更改。

思路:与上一个操作类似,第一,我们需要判断数组是否为空;第二我们需要对pos判断是否合法,这里pos的范围与上一个操作一样,最后用新的元素将pos位置原来的元素覆盖即可。

//更改某个位置的元素public void set(int pos,int value) {try {isEmpty();isIllegal_1(pos);this.array[pos] = value;}catch (PosIllegal e) {e.printStackTrace();}catch (EmptyException e) {e.printStackTrace();}}

删除第一次出现的元素

要求:将数组中第一次出现的元素删除。

思路:通过查找某个元素在顺序表的位置方法,查找要删除的元素的下标,接着让它后面的元素向前移动,把它覆盖掉,这样就达到了删除的目的,如果数组中没有这个元素,那么就是给出提示“数组中没有该元素”,最后令 size-- 即可。

//删除第一次出现的元素public void remove(int toRemove) {int x = indexOf(toRemove);if (x == -1) {   //x 等于-1说明找不到System.out.println("数组中没有该元素!");return;}for (int i = x; i < this.size - 1; i++) {this.array[i] = this.array[i + 1];}this.size--;}

清空顺序表

要求:将顺序表中的所有元素清除。

思路:在这里,我们是通过数组去实现顺序表的,而且决定顺序表中是否存在元素的是 元素个数size,那么我们直接令 size = 0即可!但这仅仅是对于基本数据类型数组而言,对于引用数据类型数组则需要将数组中的元素一个个指向null。

 //清空顺序表public void clear() {this.size = 0;}

总结

至此,我们就实现了一个简单的顺序表。

import java.util.Arrays;public class SeqList {private int[] array;private int size;//构造方法public SeqList() {}public SeqList(int initcapacity) {this.array = new int[initcapacity];}//打印顺序表public void display() {for (int i = 0; i < this.size; i++) {System.out.println(array[i] + " ");}}//获取顺序表长度public int getSize() {return this.size;}//判断顺序表是否包含某个元素,若包含则返还true,否则返回false。public boolean contains(int toFind) {for (int i = 0; i < this.size; i++) {if (array[i] == toFind) {return true;}}return false;}//查找某个元素在顺序表的位置,如果顺序表包含这个元素,就返回它在顺序表中对应的位置,否则返回-1;public int indexOf(int toFind) {for (int i = 0; i < this.size; i++) {if (array[i] == toFind) {return i;}}return -1;}//新增元素1public void add(int data) {if (isFull()) {scalingArray();}this.array[size] = data;this.size++;}//判断数组是否满了private boolean isFull() {return this.size == array.length;}//对数组进行扩容private void scalingArray() {this.array = Arrays.copyOf(this.array,2*this.array.length);//扩容为原来的两倍}//新增元素2public void add(int pos,int data) {try {isIllegal(pos);if (isFull()) {scalingArray();}for (int i = this.size - 1; i >= pos; i--) {array[i + 1] = array[i];}this.array[pos] = data;this.size++;}catch (PosIllegal e) {e.printStackTrace();}}//判断pos是否合法private void isIllegal(int pos) {if(pos < 0 || pos > this.size) {throw new PosIllegal("pos不合法!");}}//获取某个位置元素public int get(int pos) {try {isEmpty();isIllegal_1(pos);return this.array[pos];}catch (PosIllegal e) {e.printStackTrace();}catch (EmptyException e) {e.printStackTrace();}return -1;}//判断数组是否为空private void isEmpty() {if(this.size == 0) {throw new EmptyException("数组为空!");}}//判断pos是否合法2private void isIllegal_1(int pos) {if (pos < 0 || pos >= this.size) {throw new PosIllegal("pos不合法! ");}}//更改某个位置的元素public void set(int pos,int value) {try {isEmpty();isIllegal_1(pos);this.array[pos] = value;}catch (PosIllegal e) {e.printStackTrace();}catch (EmptyException e) {e.printStackTrace();}}//删除第一次出现的元素public void remove(int toRemove) {int x = indexOf(toRemove);if (x == -1) {   //x 等于-1说明找不到System.out.println("数组中没有该元素!");return;}for (int i = x; i < this.size - 1; i++) {this.array[i] = this.array[i + 1];}this.size--;}//清空顺序表public void clear() {this.size = 0;}
}

3.ArrayList类

3.1 ArrayList的简介

在集合框架中,ArrayList是一个普通的类,它继承了一些抽象类和实现了一些接口,具体如下:

【注意事项】

  1.  ArrayList是以泛型方式实现的,使用时必须要先实例化。
  2. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问。
  3. ArrayList实现了Cloneable接口,表明ArrayList是可以clone的。
  4. ArrayList实现了Serializable接口,表明ArrayList是支持序列化的。
  5. 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者 CopyOnWriteArrayList。
  6. ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表。

3.2 ArrayList的使用

ArrayList的构造

方法解释
ArrayList()无参构造
ArrayList(Collection<? extends E> c) 利用其他 Collection 构造 ArrayList
ArrayList(int initialCapacity)指定顺序表的初始容量

这里对第二种构造方法做解释:它的参数 Collection<? extends  E> c ,说明传入这个方法的参数,必须要实现Collection接口,并且要么是E,要么是E的子类

它们的使用通常如下:

import java.util.ArrayList;public class Main {public static void main(String[] args) {//创建一个空的顺序表ArrayList<Integer> arrayList1 = new ArrayList<>();//创建一个具有10个初始容量的顺序表ArrayList<Integer> arrayList2 = new ArrayList<>(10);arrayList2.add(1);arrayList2.add(2);arrayList2.add(3);//第二种构造方法的使用ArrayList<Integer> arrayList3 = new ArrayList<>(arrayList2);//因为 arrayList2 的类型也为Integer并且实现了Collection接口,因此它能作为参数传入}
}

ArrayList的常见操作

ArrayList虽然提供的方法比较多,但是常用方法如下所示:

方法解释
boolean add(E e)尾插 e
void add(int index, E element)将 e 插入到 index 位置
boolean addAll(Collection c)尾插 c 中的元素
E remove(int index)删除 index 位置元素
boolean remove(Object o)删除遇到的第一个 o
E get(int index)获取下标 index 位置元素
E set(int index, E element)将下标 index 位置元素设置为 element
void clear()清空
boolean contains(Object o)判断 o 是否在线性表中
int indexOf(Object o)返回第一个 o 所在下标
int lastIndexOf(Object o)返回最后一个 o 的下标
List subList(int fromIndex, int toIndex)截取部分 list

在这些常用方法中,除了 截取部分 的方法,其他的我们在简单顺序表中基本实现了,这里介绍一下截取部分方法。subList的参数还是很好懂的,fromIndex表示开始截取的位置,toIndex表示截取结束的位置,范围是:[fromIndex,toIndex)但是需要注意它的返回值,它的返回值是List,不是ArrayList,而List是一个接口,ArrayList是一个实现了List接口的具体类,因此使用这个方法时,需要用List的引用去接收方法的返回值。举例:

import java.util.ArrayList;
import java.util.List;public class Main_1 {public static void main(String[] args) {ArrayList<Integer> arrayList = new ArrayList<>(10);arrayList.add(1);arrayList.add(2);arrayList.add(3);arrayList.add(4);arrayList.add(5);System.out.println(arrayList);List<Integer> arrayList1 = arrayList.subList(0,2);System.out.println(arrayList1);}
}//运行结果
[1, 2, 3, 4, 5]
[1, 2]

subList需要注意的几点:

  1. 该方法返回的是原列表中从 fromIndex(包含)到 toIndex(不包含)的视图,而不是一个新的独立列表
  2. 对返回的子列表进行修改会影响原列表,反之亦然
  3. 如果原列表发生结构性修改(如添加、删除元素),再操作子列表会抛出 ConcurrentModificationException

比如说这样:

import java.util.ArrayList;
import java.util.List;public class Main_1 {public static void main(String[] args) {ArrayList<Integer> arrayList = new ArrayList<>(10);arrayList.add(1);arrayList.add(2);arrayList.add(3);arrayList.add(4);arrayList.add(5);System.out.println(arrayList);List<Integer> arrayList1 = arrayList.subList(0,2);System.out.println(arrayList1);arrayList1.set(0,99);System.out.println(arrayList);System.out.println(arrayList1);}
}//运行结果
[1, 2, 3, 4, 5]
[1, 2]
[99, 2, 3, 4, 5]
[99, 2]

 

 3.3 ArrayList的遍历

ArrayList可以使用三种方式进行遍历,分别是 for循环+下标、foreach、使用迭代器。

for循环+下标

import java.util.ArrayList;public class Test {public static void main(String[] args) {ArrayList<Integer> arrayList = new ArrayList<>();arrayList.add(1);arrayList.add(2);arrayList.add(3);arrayList.add(4);arrayList.add(5);for (int i = 0; i < arrayList.size(); i++) {System.out.print(arrayList.get(i) + " ");}}
}//运行结果
1 2 3 4 5 

foreach

import java.util.ArrayList;public class Test {public static void main(String[] args) {ArrayList<Integer> arrayList = new ArrayList<>();arrayList.add(1);arrayList.add(2);arrayList.add(3);arrayList.add(4);arrayList.add(5);for (Integer e:arrayList) {   //这里是 int e也行,因为在循环过程中会发生自动拆箱System.out.print(e + " ");}}
}

使用迭代器

迭代器是一种接口,它定义了遍历集合元素的规范,包含三个核心抽象方法:

  • boolean hasNext():判断是否还有下一个元素
  • E next():获取下一个元素
  • default void remove():删除最近一次通过 next() 获取的元素(JDK 8 后新增的默认方法)

这里我们可以使用分别使用两个迭代器去比遍历顺序表,它们是 Iterator listIterator。具体使用方式如下:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;public class Test {public static void main(String[] args) {ArrayList<Integer> arrayList = new ArrayList<>();arrayList.add(1);arrayList.add(2);arrayList.add(3);arrayList.add(4);arrayList.add(5);Iterator<Integer> it = arrayList.iterator();while(it.hasNext()) {System.out.print(it.next() + " ");}System.out.println();System.out.println("================");ListIterator<Integer> listIterator = arrayList.listIterator();while(listIterator.hasNext()) {System.out.print(listIterator.next() + " ");}}
}//运行结果
1 2 3 4 5 
================
1 2 3 4 5 

发现这里它们都可以实现遍历顺序表。

3.4 ArrayList的扩容机制

ArrayList是一个动态类型的顺序表,即:在插入元素的过程中会自动扩容。具体过程通常如下:

1.检测是否真正需要扩容,如果是调用grow准备扩容

2.预估需要库容的大小

  •         初步预估按照1.5倍大小扩容
  •         如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
  •         真正扩容之前检测是否能扩容成功,防止太大导致扩容失败

3.使用copyOf进行扩容

 3.5 ArrayList的缺点

  • ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N)
  • 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  • 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

到此,ArrayList和顺序表的内容完结!感谢您的阅读,如有错误,还请指出! 

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

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

相关文章

佳维视工业显示器在除尘与过滤设备中的应用

工业显示器凭借高可靠性、防护性能、高显示质量及多功能性&#xff0c;在除尘与过滤设备中扮演着关键角色&#xff0c;其应用贯穿设备监控、数据管理、故障诊断及远程维护全流程&#xff0c;显著提升了设备的运行效率、稳定性和智能化水平。以下是具体应用场景及优势分析&#…

svn与git Merge重要区别讲解

SVN有哪些merge方式 总的来说&#xff0c;SVN 的 merge 主要有以下 五种类型&#xff1a;1. 同步合并 (Sync Merge) / 追赶合并 (Catch-up Merge) 这是在分支开发过程中最常用的一种合并。目的&#xff1a;让你的功能分支保持最新&#xff0c;及时获取主干&#xff08;trunk&am…

Vue 3 入门教程5 - 生命周期钩子

一、生命周期概述Vue 组件从创建到销毁的整个过程称为生命周期&#xff0c;在这个过程中&#xff0c;Vue 会自动触发一系列的函数&#xff0c;这些函数被称为生命周期钩子。通过生命周期钩子&#xff0c;我们可以在组件的不同阶段执行特定的操作&#xff0c;例如初始化数据、发…

负载均衡Haproxy

简介 HAProxy是一款高性能、开源的负载均衡器与反向代理服务器&#xff0c;主要用于 HTTP、TCP等协议的流量分发&#xff0c;广泛应用于高并发、高可用的网络架构中 HAProxy是法国威利塔罗&#xff08;Willy Tarreau&#xff09;使用C语言开发的一个开源软件 企业版&#xff1a…

PostgreSQL锁机制详解:从并发控制到死锁检测

PostgreSQL锁详解 ————向逍xiangxiaohighgo.com 首先要讲锁的话&#xff0c;必须得先了解并发控制。数据库中的对象都是共享的&#xff0c;如果同时间不同的用户对同一个对象进行修改&#xff0c;就会出现数据不一致的情况。所以如果要实现并发访问&#xff0c;就需要对这…

【启发式算法】RRT*算法详细介绍(Python)

&#x1f4e2;本篇文章是博主人工智能&#xff08;AI&#xff09;领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅…

Docker架构深度解析:从核心概念到企业级实践

Docker架构深度解析&#xff1a;从核心概念到企业级实践一、Docker架构全景图1.1 整体架构示意图二、核心组件深度解析2.1 Docker Daemon工作机制三、镜像与容器原理3.1 镜像分层结构3.2 容器生命周期四、网络架构详解4.1 网络模式对比4.2 Bridge网络实现原理五、存储架构与实践…

PPT自动化 python-pptx - 8: 文本(text)

在使用 python-pptx 库操作 PowerPoint 文档时&#xff0c;理解文本的结构和处理方式至关重要。本文将深入探讨文本在形状中的组织层级、访问方式以及各级别的格式化选项。文本容器与层级结构可容纳文本的形状&#xff1a; 只有自动形状 (Auto shapes) 和表格单元格 (table cel…

使用realsense进行目标检测并标识目标深度

涉及知识点都在代码中注释了&#xff0c;直接看代码 // This example is derived from the ssd_mobilenet_object_detection opencv demo // and adapted to be used with Intel RealSense Cameras // Please see https://github.com/opencv/opencv/blob/master/LICENSE#includ…

OpenWrt Network configuration

OpenWrt Network configuration device 和 interface 关系device device 表示底层的网络设备&#xff0c;如物理网卡、桥接设备&#xff08;bridge&#xff09;、VLAN 设备等。 通过 config device 定义&#xff0c;描述设备类型、端口成员、VLAN 等属性。 例如&#xff1a;br…

VuePress 使用详解

一、核心概念 VuePress 是 Vue.js 团队开发的静态网站生成器&#xff0c;专为技术文档优化&#xff0c;具备以下特性&#xff1a; Markdown 优先&#xff1a;原生支持 Markdown 语法扩展Vue 驱动&#xff1a;可在 Markdown 中使用 Vue 组件默认主题优化&#xff1a;内置响应式…

AI大模型前沿:Muyan-TTS开源零样本语音合成技术解析

AI大模型前沿&#xff1a;Muyan-TTS开源零样本语音合成技术解析引言&#xff1a;语音合成技术的演进与Muyan-TTS的突破性意义语音合成&#xff08;Text-to-Speech, TTS&#xff09;技术作为人机交互的核心接口之一&#xff0c;自20世纪30年代贝尔实验室首次尝试电子语音合成以来…

c# everthing.exe 通信

1 获取everthing进程 调用 Everything 搜索创建SearchWithEverything函数using Microsoft.Win32; using System; using System.Diagnostics; using System.IO; using System.Management; using System.Text;class EverythingHelper {// 方法 1&#xff1a;从进程获取路径publi…

Gitee:中国企业级DevOps平台的本土化突围之路

Gitee&#xff1a;中国企业级DevOps平台的本土化突围之路 在国内数字化转型浪潮下&#xff0c;DevOps平台作为企业研发效能提升的核心引擎&#xff0c;正在经历从工具到生态的全面升级。作为国内领先的一站式DevOps解决方案&#xff0c;Gitee凭借其本土化优势与全链路服务能力&…

C++法则22:运算符 ::* 和 ->* 和 ::* 是独特的整体运算符,是不可分的。

C法则22&#xff1a;运算符 ::* 和 ->* 和 ::* 是独特的整体运算符&#xff0c;是不可分的。1. ::*&#xff08;成员指针声明符&#xff09;作用&#xff1a;用于声明一个指向类成员的指针。语法&#xff1a;ReturnType (ClassName::*pointerName) &ClassName::MemberN…

Linux系统管理习题

Linux 系统管理练习题 1.请为此虚拟机配置以下网络参数&#xff1a; 1&#xff09;主机名&#xff1a;chenyu.example.com &#xff08;将chenyu改成自己名字的全拼&#xff09; 2&#xff09;IP 地址&#xff1a;192.168.100.100/24 3&#xff09;默认网关&#xff1a;192.168…

SQL166 每天的日活数及新用户占比

SQL166 每天的日活数及新用户占比 题目理解 本SQL查询旨在分析用户活跃数据&#xff0c;计算两个关键指标&#xff1a; 每日活跃用户数(DAU)每日新增用户占比(新用户占活跃用户的比例) 解题思路 1. 数据准备阶段 首先我们需要获取所有用户的活跃记录&#xff0c;包括&…

【33】C# WinForm入门到精通 ——表格布局器TableLayoutPanel【属性、方法、事件、实例、源码】

WinForm 是 Windows Form 的简称&#xff0c;是基于 .NET Framework 平台的客户端&#xff08;PC软件&#xff09;开发技术&#xff0c;是 C# 语言中的一个重要应用。 .NET 提供了大量 Windows 风格的控件和事件&#xff0c;可以直接拿来使用。 本专栏内容是按照标题序号逐渐…

uv使用教程

以下是使用 Python 包管理工具 uv 的常见命令指南。uv 是由 Astral&#xff08;Ruff 的开发者&#xff09;开发的高性能 Python 包安装器和解析器&#xff0c;旨在替代 pip 和 pip-tools&#xff1a; 1. 安装 uv uv官网仓库 # Linux/macOS curl -Ls https://astral.sh/uv/in…

SpringBoot3.x入门到精通系列:1.1 简介与新特性

SpringBoot 3.x 简介与新特性 &#x1f4d6; 什么是SpringBoot SpringBoot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化Spring应用的初始搭建以及开发过程。SpringBoot集成了大量常用的第三方库配置&#xff0c;SpringBoot应用中这些第三方库几乎可以零配…