网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表:
链表:
💞二.顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表底层是一个数组,为什么不直接操作数组就好了,还需要单独写个类?
例如我们在数组中放置一部分元素
问在这个数组里面,有几个有效数据?
肯定有人会说3个,在Java里数组没有元素默认为0,判断的时候遇到0就停止,然后总数就是元素个数,那如果这三个元素中加了一个0呢?我们又该如何去判断呢?
这里正确的做法是用计数器,先创建一个对象,我们定义一个usedSize来记录有效的数据个数,然后添加一些方法,最后进行增删查改(CURD)。
💕2.1 接口的实现
在数组中是否可以隔着空的数组位插入元素?答案是不可以,在数据结构当中,每次储存元素的时候,一定要有一个前驱信息的,可以在两个元素中间插入
例如我们写的顺序表的相关操作:
我们还需要对他们进行完善和改进
步骤:
- 定义一个类:
public class MyArraylist {
public int[] elem;
public int usedSize;//0
private static final int DEFAULT\_SIZE = 4;//定义数组长度的
public MyArraylist(){
this.elem = new int[DEFAULT\_SIZE];
}
...
...
}
成员变量包含数组,数组里的元素个数,数组长度(可以使用final修饰让数据不被改变)
- 顺序表的打印:
public void display() {
//usedSize = 0
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.elem[i]+" ");
}
System.out.println();
}
用循环把每个元素都遍历一次,中间用空格分开
- 新增元素:
// 新增元素,默认在数组最后新增
public void add(int data) {
//1.判断是否是满的,如果满的,那么进行扩容
if(isFull()){
//扩容2倍
this.elem = Arrays.copyOf(this.elem,2\*this.elem.length);
}
//2.不满进行插入
this.elem[this.usedSize] = data;
this.usedSize++;
}
默认在数组最后新增
①需要判断数组是否是满的,满的就需要扩容,
②不满的话就进行元素插入,判断数组满不满状态的函数实现
isFull判断函数实现:
//判断当前数组是不是满的 true:满 false:空
public boolean isFull(){
if(this.usedSize == this.elem.length){
return true;
}
return false;
//在这里可以直接优化为一行代码
//return this.usedSize == this.elem.length;
}
- 在pos位置新增元素:
// 在 pos 位置新增元素
public void add(int pos, int data) {
//1.判断pos位置合法性
if(!checkPosInAdd(pos)){
throw new MyArraylistIndexOutofException("添加方法的Pos不合理!");
}
//2.判断是否是满的,如果满的,那么进行扩容
if (isFull()){
this.elem = Arrays.copyOf(this.elem,2\*this.elem.length);
}
//挪数据
for (int i = this.usedSize-1; i >= pos ; i--) {
this.elem[i+1] = this.elem[i];
}
//挪完了数据
this.elem[pos] = data;
this.usedSize++;
}
①先判断pos位置合法性,既不能是负数,又必须要有前驱信息的支持
②判断组数元素是否满了,继续调用isFull()函数
③我们插入数据的时候,需要先把插入元素后面的元素都往后挪一位,挪数据实现
从数组的最后一个元素开始往后挪,一次挪到当pos位置空出,没有元素的时候即可
④挪完数据之后,我们把pos位置赋值为data,并且把数组大小扩容一位,方便再进行新增元素
判断pos位置合法性函数checkPosInAdd实现:
private boolean checkPosInAdd(int pos){
//1.判断pos位置合法性
if(pos < 0 || pos > this.usedSize){
System.out.println("pos位置不合法");
return false;
}
return true;//合法
}
- 判定是否包含了某个元素
public boolean contains(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind){
return true;
}
}
return false;
}
- 查找某个元素对应的位置
// 查找某个元素对应的位置
public int indexOf(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind){
return i;
}
}
return -1;
}
- 获取pos下标的元素
// 获取 pos 位置的元素
public int get(int pos) {
if(!checkPosInGet(pos)){
throw new MyArraylistIndexOutofException("获取pos下标时,位置不合法!");
}
//判断是否为空(可有可无)
if(isEmpty()){
throw new MyArrayListEmptyException("获取元素的时候,顺序表为空!");
}
return this.elem[pos];
}
①先判断pos位置合法性
②判断数组是否为空(可有可无)
判断pos位置合法性函数checkPosInGet实现:
private boolean checkPosInGet(int pos){
//1.判断pos位置合法性
if(pos < 0 || pos >= this.usedSize){
System.out.println("pos位置不合法");
return false;
}
return true;//合法
}
判断数组是否为空isEmpty函数实现:
private boolean isEmpty(){
return this.usedSize == 0;
}
- 给pos位置的元素替换成value
①先要进行合法性判断再替换
// 给 pos 位置的元素设为 value
public void set(int pos, int value) {
if(!checkPosInGet(pos)){
throw new MyArraylistIndexOutofException("更新pos下标的元素,位置不合法!");
}
//如果合法,那么其实不用判断顺序表为空的状态了
if(isEmpty()){
throw new MyArrayListEmptyException("顺序表为空!");
}
//顺序表为满的情况也可以更新
this.elem[pos] = value;
}
- 删除第一次出现的关键字key
//删除第一次出现的关键字key
//判断条件:1.顺序表不为空 2.顺序表当中有我们要删除的元素 3.找到它的下标 4.把i+1的值赋给i,i还要小于usedSize-1
public void remove(int key) {
if(isEmpty()){
throw new MyArrayListEmptyException("顺序表为空,不能删除!");
}
int index = indexOf(key);
if(index == -1){
System.out.println("不存在你要找的数据!");
return;
}
for (int i = index; i < this.usedSize-1; i++) {
this.elem[i] = this.elem[i+1];
}
//删除完成
this.usedSize--;
this.elem[usedSize] = 0;//此处不能置为null是因为elem是int类型 如果是引用类型则置为null
}
①顺序表不为空
②顺序表当中有我们要删除的元素
③找到它的下标
④把i+1的值赋给i,i还要小于usedSize-1
(只要涉及到删除数据,如果是引用数据类型,那么就要把elem[i] = null;否则就会发生内存泄漏)
- 获取顺序表长度
// 获取顺序表长度
public int size() {
return this.usedSize;
}
- 清空顺序表
// 清空顺序表
public void clear() {
//因为是基本类型,所以置为0即可
this.usedSize = 0;
/\*当它是引用类型时
for (int i = 0; i < this.usedSize; i++) {
this.elem[i] = null;
}
this.usedSize = 0;
\*/
}
①基本类型置为0即可,若是引用类型则循环打印置为null,再置为0
注意:
此处可以把elem置为null可以吗?可以,但是很暴力,数组直接被回收了,顺序表只执行了一次就没了,再次使用的时候还需开辟新的数组,相当于我们每次使用的时候还需new一次,很麻烦也没必要
在这里面添加,获取pos下标不合法的时候我们也可以写我们需要的异常类来更好的实现我们需要的异常,实现异常的抛出是我们赋值命名的异常名:
public class MyArrayListEmptyException extends RuntimeException{
public MyArrayListEmptyException(){
}
public MyArrayListEmptyException(String message){
super(message);
}
}
public class MyArraylistIndexOutofException extends RuntimeException{
public MyArraylistIndexOutofException(){
}
public MyArraylistIndexOutofException(String message){
super(message);
}
}
在主函数里对顺序表的操作:
public static void main(String[] args) {
MyArraylist myArraylist = new MyArraylist();
myArraylist.add(0,1);//给0下标赋值为1
myArraylist.add(1,2);
myArraylist.add(2,3);
myArraylist.add(3,4);
myArraylist.add(4,5);
myArraylist.display();//输出顺序表
myArraylist.set(4,199);//把下角标为4的元素替换成199
myArraylist.display();
System.out.println("=============");
myArraylist.clear();//清空顺序表
myArraylist.display();
}
⚡️三. ArrayList简介
在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:
- ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
- ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
- ArrayList实现了Serializable接口,表明ArrayList是支持序列化的
- 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList
- ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表
🎶四. ArrayList使用
🎵4.1 ArrayList的构造
方法一ArrayList()不带参数的构造方法的使用:
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);//往数组最后的一个位置存元素
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
System.out.println(arrayList);//用字符串的形式打印出来所有的元素
System.out.println(arrayList.size());//获取当前有效数据的个数
System.out.println(arrayList.get(1));//获取指定下标的元素
方法二的使用:
ArrayList<Integer> arrayList2 = new ArrayList<>(arrayList);
arrayList2.add(99);
arrayList2.add(199);
System.out.println(arrayList2);
arrayList2承接了arrayList1的数据(使用其他的集合 来构造当前的List,底层源码实现是数组的拷贝)
方法三的使用:
ArrayList<Integer> arrayList3 = new ArrayList<>(15);
指定初始化数组容量大小
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
m.out.println(arrayList2);
>
> arrayList2承接了arrayList1的数据(使用其他的集合 来构造当前的List,底层源码实现是数组的拷贝)
>
>
>
方法三的使用:
ArrayList arrayList3 = new ArrayList<>(15);
>
> 指定初始化数组容量大小
>
>
>
>
[外链图片转存中...(img-6S4nsrv6-1715501716574)]
[外链图片转存中...(img-18JIaOG3-1715501716574)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**