【Java数据结构】初步认识ArrayList与顺序表

前言~🥳🎉🎉🎉   

hellohello~,大家好💕💕,这里是E绵绵呀✋✋ ,如果觉得这篇文章还不错的话还请点赞❤️❤️收藏💞 💞 关注💥💥,如果发现这篇文章有问题的话,欢迎各位评论留言指正,大家一起加油!一起chin up!👍👍 

💥个人主页:E绵绵的博客
💥所属专栏:JAVA知识点专栏   JAVA题目练习  c语言知识点专栏   c语言题目练习

❤️❤️这篇文章我们就正式开始数据结构的学习,学习其中的顺序表结构。出发吧!

参考文章:Java【顺序表】详细图解模拟实现 + 【ArrayList】常用方法介绍_java顺序表逻辑图-CSDN博客

线性表 

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

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

顺序表 

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。 并且顺序表是一个动态数组,可以存储不同的元素,还可以根据需要自动调整大小。

顺序表的模拟实现

❤️❤️为什么要模拟实现:
自己模拟实现 简易版的 顺序表的增删查改等主要功能,大致理解顺序表的设计思想
再对比学习 Java 提供的集合类当中的 ArrayList ,在学习 Java 的 ArrayList 常用方法的同时,也能学习源码的思想。

成员属性 

Java 中的 ArrayList(顺序表) 是集合框架中的一个类,要模拟实现顺序表,也得自己实现一个类,首先要考虑这个类中的成员属性。

之前就已经说过,顺序表底层是基于数组实现的,那么成员属性就需要:
1️⃣数组 array:来存放数据
2️⃣变量 capacity :来记录数组的容量,当数组中的存放的数据满了就需要增大容量
3️⃣变量 useSize:来记录数组已经存放了几个数据

❤️❤️为了体现封装思想,成员属性全部设置为 private

public class SeqList {
    private int[] array;// 数组
    private int capacity;// 容量
    private int useSize;// 当前数组存放的数据的个数
}

⚠️顺序表的模拟实现重在理解理想,为了简便,我们不使用泛型🙅,数组中存放 int 类型

成员方法 

❤️❤️如下是模拟顺序表的成员方法,我们通过这些成员方法来模拟顺序表的功能,我们现在来一一实现这些方法。

// 新增元素,默认在数组最后新增
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) { }

//删除第一次出现的关键字key
public void remove(int toRemove) { }

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

// 清空顺序表
public void clear() { }

// 打印顺序表,注意:ArrayList 没有这个方法,为了方便看测试结果给出的
public void display() { }

1.构造方法

 构造方法的作用:初始化成员属性,useSize 无需初始化,编译器默认初始化为 0

    // 将顺序表的底层容量设置为capacity
    public SeqList(int capacity){
        this.capacity = capacity;
        // 为数组开辟内存空间 
        this.array = new int[this.capacity];
    }
    public SeqList() {
        this.capacity=10;
        this.array=new int[10];
       //当无参数时,默认创建一个为容量为10的数组
    }

 2,add——新增元素,默认在数组末尾新增

❗️❗️在末尾新增数据之前,必须考虑:顺序表是否已🈵️,如果满了,则需要扩容之后再添加元素 。

所以我们需要分别写出 判满 和 扩容 这两个方法:


 2.1, isFull——判断顺序表是否已满 
    public boolean isFull() {
        return this.useSize == this.capacity;
    }

  2.2, expandCapacity——扩容
  public  void expandCapacity(){
       this.array =Arrays.copyOf(this.array,capacity*2);
       capacity*=2;
    }
}

利用 copyOf 方法,拷贝数组并将新数组容量扩大两倍,让 this.array 引用新的数组。


 ✅所以 add 方法的写法为: 

    public void add(int data) {
        // 判满
        if (isFull()) {
            // 满了就扩容
            expandCapacity();
        }
        this.array[this.useSize] = data;
        this.useSize++;
    }

最后记得,增加数据之后,**useSize 也要++**❗️❗️ 


3.add——在 pos 位置新增元素

 ❗️❗️这个方法是:在指定位置新增元素,新增之前必须考虑:
1️⃣顺序表是否已满
2️⃣pos 位置是否合法

3.1, judgeAddPos——判断 add 时pos 位置合法性
判满的方法以及写过了,现在我们需要补充: 判断 pos 位置合法性 的方法
需要思考,pos 在什么位置才是合法呢? 

这就要提一个知识点了:在数据结构中,我们每次往一个数据结构里存储数据时,该数据必须有一个前驱信息(前驱是指该元素的前一个元素),否则不能存放。

所以我们举个例子,如下:

像该情况我们的pos自然不能小于0,而又因为数组中存储数据时,该数据必须要有前驱信息,所以不能大于3。所以pos不能小于0或大于3.


所以在这我们可以得出以下代码

public void judgeAddPos(int pos){
        if(pos<0||pos>useSize){
            throw new ArrayListIndexOutOfException("pos位置不合法");
        }
    }
}

如果 pos 参数不合法,就不能执行下面的代码,抛出一个异常,所以我们可以自定义一个异常类

// 继承于运行时异常
public class ArrayListIndexOutOfException 
                 extends RuntimeException {
    public ArrayListIndexOutOfException(String str) {
        super(str);
    }
}

这样,当我们 add 时的 pos 参数不合法时,就会抛出异常。

“准备工作” 做足之后,我们需要考虑,如何实现在 pos 位置新增,也就是如何去插入?

🚗🚗🚗
就是把 pos 下标以及之后 的数据 向后依次 覆盖,最终把 pos 位置“空出来”,放入新数据:

 

  public void add(int pos, int data) {
        try{
            judgeAddPos(pos);}
        catch(Exception e){
            e.printStackTrace();
            return;
        }
        if(isFull()){
            expandCapacity();
        }
        for (int i = useSize-1; i >pos-1 ; i--) {
          array[i+1]=array[i];
        }
        array[pos]=data;
        useSize++;
    }

我们之所以在这用try-catch是为了防止出现这个错误后就直接导致程序崩溃,运行不了后面的程序,所以我们用try-catch捕获它,就能在发生这个错误后依然能运行这个程序。


⚠️注意:
要先移动 3,再移动 2 ——从后往前的顺序移动
如果先移动 2 ,则会把 3 覆盖掉,丢失数据。

最后不要忘记 useSzie++ ❗️❗️

4.contains——判定是否包含某个元素

比较简单,遍历整个数组即可

public boolean contains(int toFind) {
        for (int i = 0; i <useSize ; i++) {
            if(array[useSize]==toFind)
                return  true;
        }
        return false;
    }

因为这里我们存放的是 int 类型的变量,但 ArrayList 当中可以存放引用数据类型的
⚠️⚠️⚠️当表中是引用类型时,就不可以用“等号”比较,应该用 equals 方法

5, indexOf——查找某个元素对应的位置 

还是遍历数组,不过这里如果没找到该元素,则要抛出异常,我们上面讲过类似的,这里就不重复讲了 。在这我们又自定义了一个异常类。

class  NotFindPos extends RuntimeException{
    public  NotFindPos(String message){
    super(message);
    }
}
    public int indexOf(int toFind) {
        for (int i = 0; i <useSize ; i++) {
            if(array[i]==toFind)
                return i;
        }
        try{
            throw  new NotFindPos("不存在该数"+toFind);   
        }catch (Exception e){
            return -1;
        }
    }

因为这里我们存放的是 int 类型的变量,但 ArrayList 当中可以存放引用数据类型的
⚠️⚠️⚠️当表中是引用类型时,就不可以用“等号”比较,应该用 equals 方法

6.get——获取 pos 位置的元素 

在获取 pos 之前必须保证 pos 位置合法性,但此时的 pos 判断合法性和 add 时的判断规则不一样❗️❗️


🚗🚗🚗
获取 pos 位置的元素,前提是 pos 位置上有数据
此时 pos 的合法性判断规则是:pos 不能小于 0 或 不能大于 useSize - 1

 6.1,judgePos——判断 pos 位置合法性
    public  void judgePos(int pos){
        if(pos<0||pos>useSize-1){
                throw new ArrayListIndexOutOfException("pos位置不合法");
    }
}}

如果pos 不合法,抛出异常❌

  做好 “准备工作” 之后, get 方法就很简单了

  public int get(int pos) {
        try {
            judgePos(pos);
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
        return array[pos];
}

 7, set——给 pos 位置的元素设为 value

老规矩,pos 作为参数时,就要判断合法性❗️❗️代码很简单。

 public void set(int pos, int value) {
        try{
            judgePos(pos);
        }catch(Exception e) {
            e.printStackTrace();
            return;
        }
        array[pos]=value;
        }

8,remove——删除第一次出现的数据 

我们前面分析过了 add 方法的执行原理,那么删除的原理恰好是和 add 的操作相反:在数组中要 “删除” 一个数,让后面的数据依次向前覆盖即可,对于这个删除操作,我们在图书管理器中碰见过相同的操作,其是一样的思路。 

可以看到最后还剩一个 3,没有必要处理,useSize- - 即可✅
别忘了,怎么找到待删除数据的位置呢❓——调用前面写过的 indexOf 方法❗️

    public void remove(int toRemove) {
        int pos = indexOf(toRemove);
        if (pos == -1) {
        // 找不到的情况
            System.out.println("不存在该数据");
        }else {
            // 注意这里的循环条件
            for (int i = pos; i < this.useSize - 1; i++) {
                this.array[i] = this.array[i + 1];
            }
            this.useSize--;
        }
    }

  ⚠️注意:
 i < this.useSize - 1 这里不能写成 <=,当数组正好是满的情况下
 this.array[i] = this.array[i + 1]; 这里访问 i+1 下标就会数组越界

9,size——获取顺序表长度 

直接返回 useSize 即可 

public int size() {
        return useSize;
    }

10.clear——清空顺序表 

因为该数组存放的内容为基本类型,所以我们只需要将usesize变为0就行。

    public void clear() {
        this.useSize = 0;
    }

如果存放的是引用类型,不仅要将useSize变为0,还要将数组中存放的引用变量指向的空间全释放掉。释放掉的方法就是将数组存放的引用变量全指向null,当这些空间没有引用变量指向时,就会自动释放掉。

之所以要释放这些空间是为了防止内存泄漏,提高空间的利用率。


ArrayList类中的clear方法就是一个很好的例子,如下:(因为其数组存放的是引用变量)

11,display——打印顺序表 

注意:顺序表中不存在该方法,我们这是为了方便看测试结果自己加的。

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

 ArrayList的模拟实现总代码

import java.sql.Array;
import java.util.ArrayList;
import java.util.Arrays;

public class SeqList {
    private int[] array;// 数组
    private int capacity;// 容量
    private int useSize;// 当前数组存放的数据的个数

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

    }

    public SeqList() {
        this.capacity = 10;
        this.array = new int[10];
        //当无参数时,默认创建一个为容量为10的数组
    }

    // 新增元素,默认在数组最后新增
    public void add(int data) {
        // 判满
        if (isFull()) {
            // 满了就扩容
            expandCapacity();
        }
        this.array[this.useSize] = data;
        this.useSize++;
    }


    // 在 pos 位置新增元素
    public void add(int pos, int data) {
        try {
            judgeAddPos(pos);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        if (isFull()) {
            expandCapacity();
        }
        for (int i = useSize - 1; i > pos - 1; i--) {
            array[i + 1] = array[i];
        }
        array[pos] = data;
        useSize++;
    }

    // 判定是否包含某个元素
    public boolean contains(int toFind) {
        for (int i = 0; i < useSize; i++) {
            if (array[useSize] == toFind)
                return true;
        }
        return false;
    }

    // 查找某个元素对应的位置
    public int indexOf(int toFind) {
        for (int i = 0; i < useSize; i++) {
            if (array[i] == toFind)
                return i;
        }
        try {
            throw new NotFindPos("不存在该数" + toFind);
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    // 获取 pos 位置的元素
    public int get(int pos) {
        try {
            judgePos(pos);
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
        return array[pos];
}
    // 给 pos 位置的元素设为 value
    public void set(int pos, int value) {
        try{
            judgePos(pos);
        }catch(Exception e) {
            e.printStackTrace();
            return;
        }
        array[pos]=value;
        }


    //删除第一次出现的关键字key
    public void remove(int toRemove) {
            int pos = indexOf(toRemove);
            if (pos == -1) {
                // 找不到的情况
                System.out.println("不存在该数据");
            }else {
                // 注意这里的循环条件
                for (int i = pos; i < this.useSize - 1; i++) {
                    this.array[i] = this.array[i + 1];
                }
                this.useSize--;
            }
        }
    // 获取顺序表长度
    public int size() {
        return useSize;
    }

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

    // 打印顺序表,注意:ArrayList 没有这个方法,为了方便看测试结果给出的
    public void display() {
        for (int i = 0; i <useSize ; i++) {
            System.out.print(array[i]+" ");
        }
        System.out.println();
    }

    public boolean isFull() {
      return  this.capacity == this.useSize;
    }
    public  void expandCapacity(){
       this.array =Arrays.copyOf(this.array,capacity*2);
       capacity*=2;
    }
    public void judgeAddPos(int pos){
        if(pos<0||pos>useSize){
            throw new ArrayListIndexOutOfException("pos位置不合法");
        }
    }

    public  void judgePos(int pos){
        if(pos<0||pos>useSize-1){
                throw new ArrayListIndexOutOfException("pos位置不合法");
    }
}}


class  ArrayListIndexOutOfException extends  RuntimeException{
    public ArrayListIndexOutOfException(String message) {
        super(message);
    }
}
class  NotFindPos extends RuntimeException{
    public  NotFindPos(String message){
    super(message);
    }
}

 模拟的顺序表SeqList的使用

class Test{
    public static void main(String[] args) {
        SeqList seqList=new SeqList();
        seqList.add(0,4);
        seqList.add(1);
        seqList.add(4);
        seqList.display();
        seqList.remove(1);
        seqList.display();
        seqList.clear();
        seqList.add(1);
        seqList.display();
        seqList.add(5);
        System.out.println(seqList.contains(5));
        seqList.set(1,6);
        seqList.display();
        System.out.println(seqList.contains(5));
        System.out.println(seqList.indexOf(2));
        System.out.println(seqList.get(0));
        seqList.set(4,3);
        seqList.add(2,7);
       seqList.display();
        System.out.println(seqList.size());
    }

}

对于其打印如下:


❤️❤️我们看显示结果可知这代码的确没问题。所以对于这模拟的顺序表我们就模拟成功了.你们自己也可以使用下该代码,看下是否有误,如果有误欢迎大佬来评论区指点一下。

 总结

所以这就是我们的顺序表第一部分内容,我们在这主要对顺序表进行了模拟,使我们在之后的学习中能更加理解顺序表的源码,学习其源码思想。在之后的顺序表第二部分我们将给大家介绍真正的顺序表ArrayList,敬请期待! 还希望各位大佬们能给个三连,点点关注,点点赞,发发评论呀,感谢各位大佬~❤️❤️💕💕🥳🎉🎉🎉

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flutter开发好用插件url_launcher详解-启动 URL

文章目录 url_launcher介绍安装用法错误处理自定义行为其他功能 url_launcher介绍 url_launcher 是一个 Flutter 插件&#xff0c;用于启动 URL。它支持网络、电话、短信和电子邮件方案。您可以使用它从您的 Flutter 应用程序中打开网站、拨打号码、发送短信或撰写电子邮件。 …

jvm知识点总结(二)

Java8默认使用的垃圾收集器是什么? Java8版本的Hotspot JVM,默认情况下使用的是并行垃圾收集器&#xff08;Parallel GC&#xff09; 如果CPU使用率飙升&#xff0c;如何排查? 1.先通过top定位到消耗最高的进程id 2.执行top -h pid单独监控该进程 3.在2中输入H&#xff…

【线性代数 C++】求逆矩阵

对于 n n n阶矩阵 A A A&#xff0c;如果有 n n n阶矩阵 B B B&#xff0c;使 A B B A E ABBAE ABBAE&#xff0c;则说 A A A是可逆的&#xff0c;并把 B B B称为 A A A的逆矩阵. A A A的逆矩阵记作 A − 1 A^{-1} A−1&#xff0c;则 B A − 1 BA^{-1} BA−1.若 ∣ A ∣ ≠…

二、OSPF协议基础

基于SPF算法&#xff08;Dijkstra算法&#xff09;的链路状态路由协议OSPF&#xff08;Open Shortest Path First&#xff0c;开放式最短路径优先&#xff09; 目录 1.RIP在大型网络中部署所面临的问题 2.Router ID 3.OSPF的报文 4.OSPF邻居建立过程 5.OSPF报文的确认机制…

59、回溯-括号生成

思路&#xff1a; 括号是成对出现&#xff0c;首先左括号可以放n个&#xff0c;右括号也可以放n个。如果当前左括号放了3了&#xff0c;右括号放了4个&#xff0c;错了&#xff0c;如果左括号放了5个&#xff0c;右括号放了4个。可以&#xff0c;继续放右括号即可。所以可以设…

linux系统安全及应用【上】

目录 1.账号安全控制 1系统账号清理 2密码安全控制 1 对已经存在的用户账号进行控制 2 对新建的用户密码默认设置 3 历史命令和终端自动注销的安全管理 1 历史命令的限制 2. 用户切换管理 1 su命令的使用 2 ssh 3.授权用户管理 1 sudo命令 2 sudo用户别名 3 查看su…

Vuforia AR篇(三)— AR模型出场效果

目录 前言一、AR模型出场二、AR出场特效三、添加过渡效果四、效果 前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器学习&#xff0c;本文就介绍了机器学习的基础内容。 一、AR模型出场 创建ARCamer…

【Go语言快速上手(四)】面向对象的三大特性引入

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Go语言专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Go语言知识   &#x1f51d;&#x1f51d; GO快速上手 1. 前言2. 初识GO中的结构…

深度学习中的子空间、线性变换和矩阵概念应用

1.表示子空间 在深度学习中&#xff0c;“不同的表示子空间”通常是指模型通过不同的参数&#xff08;例如权重矩阵&#xff09;将输入数据映射到不同的高维空间&#xff0c;这些空间被称为表示子空间。每个子空间都能够捕获输入数据中不同的特征或模式。以下是一些详细解释&am…

软考-论文写作-论架构风格论文

题目 素材 框架 一、 摘要 2020年12月,我参加了某省政协委员履职系统的开发。该系统为政协机关人员线上开展各项工作以及委员完成各项履职提供了全方位的软件支撑。我在该项目重担任系统架构师一职,负责履职系统的架构设计。本文结合实践,以委员履职系统为例,主要讨论软件…

使用FunASR处理语音识别

FunASR是阿里的一个语音识别工具&#xff0c;比SpeechRecognition功能多安装也很简单&#xff1b; 官方介绍&#xff1a;FunASR是一个基础语音识别工具包&#xff0c;提供多种功能&#xff0c;包括语音识别&#xff08;ASR&#xff09;、语音端点检测&#xff08;VAD&#xff…

verilog中比较器的代码用法

在 verilog 中以大于“>”&#xff0c;等于””&#xff0c;小于”<”&#xff0c;大于等于”>”&#xff0c;小于等于”<”&#xff0c;不等于”!”表示&#xff0c;以大于举例&#xff0c;如 c a > b ;表示如果 a 大于 b&#xff0c;那么 c 的值就为 1&#x…

网盘——文件重命名

文件重命名具体步骤如下&#xff1a; 目录 1、具体步骤 2、代码实现 2.1、添加重命名文件的槽函数 2.2、关联重命名文件夹信号槽 2.3、添加重命名文件的协议 2.4、添加槽函数定义 2.5、服务器 2.6、添加重命名文件的case 2.7、客户端接收回复 3、测试 3.1、点击重命…

【AIGC调研系列】Bunny-Llama-3-8B-V与其他多模态大模型相比的优劣

Bunny-Llama-3-8B-V作为基于Llama-3的多模态大模型&#xff0c;其优势主要体现在以下几个方面&#xff1a; 性能超越其他模型&#xff1a;根据我搜索到的资料&#xff0c;Bunny-Llama-3-8B-V在多个主流Benchmark上表现良好&#xff0c;超越了LLaVA-7B、LLaVA-13B、Mini-Gemini…

汽车企业安全上网解决方案

需求背景 成立于1866年的某老牌汽车服务独立运营商&#xff0c;目前已经是全球最大的独立汽车服务网络之一&#xff0c;拥有95年的历史&#xff0c;在全球150多个国家拥有17,000多个维修站&#xff0c;始终致力于为每一位车主提供高品质&#xff0c;可信赖的的专业汽车保养和维…

智慧文旅:引领旅游产业智慧升级的创新模式

一、智慧文旅是什么&#xff1f; 智慧文旅是指以当地特色文化为核心&#xff0c;借助现代科技手段&#xff0c;实现旅游景区全面智慧升级的旅游模式。在智慧文旅中&#xff0c;新一代信息网络技术和装备得到充分运用&#xff0c;文化旅游基础设施得到新建和改善&#xff0c;特…

OpenCV鼠标绘制线段

鼠标绘制线段 // 鼠标回调函数 void draw_circle(int event, int x, int y, int flags, void* param) {cv::Mat* img (cv::Mat*)param;if (event cv::EVENT_LBUTTONDBLCLK){cv::circle(*img, cv::Point(x, y), 100, cv::Scalar(0, 0, 255), -1);} }// 鼠标回调函数 void dra…

牛客NC199 字符串解码【中等 递归,栈的思想 C++/Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/4e008fd863bb4681b54fb438bb859b92 相同题目&#xff1a; https://www.lintcode.com/problem/575 思路 解法和基础计算器1&#xff0c;2,3类似,递归参考答案C struct Info {string str;int stopindex;Info(str…

react —— useState 深入

基础用法 useState Hook 提供了这两个功能&#xff1a; State 变量 在第一次重新渲染期间&#xff0c;这将具有作为参数传递的值State setter 函数 set 函数将允许将状态的值更新为不同的值&#xff0c;如果 set 函数中提供的值不同&#xff0c;则将触发重新渲染。 注意&…

【网站项目】书籍销售系统小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…