数据结构之ArrayList与顺序表(上)

找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程(ಥ_ಥ)-CSDN博客

所属专栏:数据结构(Java版)

顺序表的学习,点我

上面这篇博文是关于顺序表的基础知识,以及顺序表的实现。

目录

手动实现顺序表的源码:

分析Java 8 的 ArrayList 的源码 

字段:

构造方法:

ArrayList本身的扩容机制和分配内存机制

ArrayList常见操作

ArrayList的遍历 

普通for循环遍历

for-each遍历 

toString方法遍历 

迭代器遍历 


手动实现顺序表的源码:

下面是Java版的顺序表源码:

public class MyArraylist {
    public int[] elem;
    public int usedSize;//0
    //默认容量
    private static final int DEFAULT_SIZE = 10;

    public MyArraylist() {
        this.elem = new int[DEFAULT_SIZE];
    }

    /**
     * 打印顺序表:
     *   根据usedSize判断即可
     */
    public void display() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.print(this.elem[i]+" ");
        }
        System.out.println();
    }

    // 新增元素,默认在数组最后新增
    public void add(int data) {
        // 增加元素之前得先判断数组是否已满
        if (isFull()) {
            expansion();
        }
        // 尾插数据不需要判断 pos 位置是否合法
        elem[this.usedSize++] = data;
    }

    // 扩容
    private void expansion() {
        // 原来数组的2倍扩容
        this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
    }

    /**
     * 判断当前的顺序表是不是满的!
     * @return true:满   false代表空
     */
    public boolean isFull() {
        // 判断这个数组的有效元素个数是否等于数组的长度
        return this.usedSize == this.elem.length;
    }


    private boolean checkPosInAdd(int pos) {
        if (pos < 0 || pos > this.usedSize) {
            return false;
        }
        return true;//合法
    }

    // 在 pos 位置新增元素
    public void add(int pos, int data) throws PosofAddException{
        if (!checkPosInAdd(pos)) {
            throw new PosofAddException("add(int pos, int data) " +
                    "pos位置不合法");
        }
        // 判断是否为尾插
        if (pos == this.usedSize) {
            add(data);
        }else {
            // 开始移除元素(从后往前移除)
            for (int i = this.usedSize; i > pos; i--) {
                this.elem[i] = this.elem[i-1];
            }
            // 开始插入数据
            this.elem[pos] = data;
            this.usedSize++;
        }
    }

    // 判定是否包含某个元素
    public boolean contains(int toFind) {
        // 先判断这个数组是否有元素
        if (isEmpty()) {
            return false;
        }
        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 位置的元素
    public int get(int pos) throws PosofGetException{
        if (pos < 0 || pos > this.usedSize) {
            throw new PosofGetException("get(int pos)" +
                    "pos位置不合法");
        }
        return this.elem[pos];
    }

    private boolean isEmpty() {
        // 有效数据个数为0,就是为空
        return this.usedSize == 0;
    }

    // 给 pos 位置的元素设为【更新为】 value
    public void set(int pos, int value) throws PosofSetException{
        // 先得判断这个 pos 位置是否合法
        if (pos < 0 || pos >= this.usedSize) {
            throw new PosofSetException("set(int pos, int value)" +
                    "要修改的位置不合法");
        }
        this.elem[pos] = value;
    }

    /**
     * 删除第一次出现的关键字key
     * @param key
     */
    public void remove(int key) throws PosofRemoveException{
        int pos = indexOf(key); // 下标
        if (pos < 0) {
            throw new PosofRemoveException("remove(int key)" +
                    "没有您要删除的数据");
        }
        for (int i = pos; i < this.usedSize - 1; i++) {
            // 后一个位置往前覆盖
            this.elem[i] = this.elem[i+1];
        }
        this.usedSize--;
    }

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

    // 清空顺序表
    public void clear() {
        // 由于存放的是基本数据类型,直接清空即可
        this.usedSize = 0;
        // 如果存放的是引用数据类型就得通过for循环遍历把数组的内容置为null
        // 注意:如果直接把数组置为null的话,就会存在安全问题,而且源码也是遍历的方式
    }
}

异常:

PosofAddException:

public class PosofAddException extends RuntimeException{
    public PosofAddException() {

    }
    public PosofAddException(String msg) {
        super(msg);
    }
}

 PosofGetException:

public class PosofGetException extends RuntimeException{
    public PosofGetException() {

    }
    public PosofGetException(String msg) {
        super(msg);
    }
}

PosofSetException: 

public class PosofSetException extends RuntimeException{
    public PosofSetException() {

    }
    public PosofSetException(String msg) {
        super(msg);
    }
}

PosofRemoveException: 

public class PosofRemoveException extends RuntimeException{
    public PosofRemoveException() {

    }
    public PosofRemoveException(String msg) {
        super(msg);
    }
}

分析Java 8 的 ArrayList 的源码 

实现了咱们自己写的顺序表了之后,就该来看Java本身的源码是怎么写的以及与我们的有什么不同?(注意:由于Java 17封装性太强,不好观看源码,因此下面的源码来自Java 8。)

字段:

elementData 就是我们自己实现的 elem 数组。

size 就是 usedSize ,也就是这个数组的有效元素的个数。 

构造方法:

Java 8 实现的顺序表有三个构造方法。 

带有一个 int 类型的参数的构造方法: 

 不带参数的构造方法:

带一个 Collection 类型的参数的构造方法:

下面的是其源码: 

首先,我们得知道Collection 到底是这个啥? 

根据上面这个图可以得知:Collection 是一个接口。

上面的参数先不看泛型,那就是Collection c  这个意味着只要实现了Collection 接口,就可以被当成实参传过来。而从上图的关系可知:ArrayList 继承了 AbstractLIst 这个抽象类 ,并且实现了LIst这个接口,而 LIst 这个接口拓展了 Collection 这个接口的功能。也就意味着 ArrayList 这个类实现了 Collection 这个接口。那么 ArrayList 就可以被当成参数传过来。

接下来,就是了解 <? extends E> ,?就可以当成是被传过来的 ArrayList 中的泛型,也就是说被传过来的 ArrayList 中的泛型要是 E 或者 E的子类。例如:

了解了这些,我们就来看源码吧。这个源码的大概意思是把传入的 ArrayList 中的元素给全部拷贝到原来的 ArrayList 的后面。

ArrayList本身的扩容机制和分配内存机制

既然我们在创建一个顺序表的时候,原本是空,那么当我们去增加元素的时候,扩容的机制又是如何呢?

上面是分析过程(可忽略),总之,得出了下面这个结论: 

当顺序表为空时,我们如果去增加元素,就会初始化一个大小10的数组。并且数组在满了之后,会按照1.5倍的扩容方式来扩容。如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容。

ArrayList常见操作

ArrayList常用方法
boolean add(E e)尾插e
void add(int index, E element)将 e 插入到 index 位置
boolean addAll(Collection<? extends E> 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)判断是否在顺序表中
int indexOf(Object o)返回第一个o所在下标
int lastlndexOf(Object o)返回最后一个o的下标
List<E> subList(int fromlndex, int tolndex)截取部分list

大部分方法,我们都很熟悉。因此这里只展示 addAll()方法 和 subList 方法。

addAll () 方法可以实现 ArrayList 的带Collection 类型的参数的构造方法。

从上面打印的结果可以看出来,ArrayList 是重写了toString 方法的。

从这里我们就可以看出来,被分割的数组应该是下面这样:

ArrayList的遍历 

普通for循环遍历

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        for (int i = 0; i < arrayList.size(); i++) {
            System.out.print(arrayList.get(i)+" ");
        }
    }
}

for-each遍历 

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        for (Integer x : arrayList) {
            System.out.print(x+" ");
        }
    }
}

toString方法遍历 

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        System.out.println(arrayList.toString());
    }
}

迭代器遍历 

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        // 调用iterator方法生成一个迭代器,用Iterator<E>来接收
        Iterator<Integer> integerIterator = arrayList.iterator();
        // 如果下一个元素有值话,就进入while循环,并且打印下一个值,最后自己往后走
        while (integerIterator.hasNext()) {
            System.out.print(integerIterator.next()+" ");
        }
    }
}
        

只要是实现了 Iterator 接口就可以使用迭代器的方式来遍历。就是下面这张图:

好啦!本期 数据结构之ArrayList与顺序表(上)的学习就到此结束啦!我们下一期再一起学习吧!

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

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

相关文章

美团面试:百亿级分片,如何设计基因算法?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的架构类/设计类的场景题&#xff1a; 1.说说分库分表的基因算法&#xff1f…

【Linux】深入解析动静态库:原理、制作、使用与动态链接机制

文章目录 前言&#xff1a;1. 什么是动静态库2. 动静态库的制作和使用3. 动态库的查找问题4. 理解动态库的加载4.1. 站在系统的角度理解4.2. 编址、可执行程序4.3. 动态库动态链接和加载问题 总结&#xff1a; 前言&#xff1a; 在软件开发中&#xff0c;动静态库是两种重要的…

Netty中的ByteBuf使用介绍

ByteBuf有三类&#xff1a; 堆缓存区&#xff1a;JVM堆内存分配直接缓冲区&#xff1a;有计算机内存分配&#xff0c;JVM只是保留分配内存的地址信息&#xff0c;相对于堆内存方式较为昂贵&#xff1b;复合缓冲区&#xff1a;复合缓冲区CompositeByteBuf&#xff0c;它为多个B…

6月26~28日,2024北京国际消防展即将开幕!

随着社会的快速发展&#xff0c;消防安全日益受到广大民众的高度关注。为了进一步推动消防科技的创新与发展&#xff0c;提升全民消防安全意识&#xff0c;2024年北京消防展将于6月26日在北京国家会议中心盛大开展。目前:观众预登记已全面启动&#xff0c;广大市民和业界人士可…

Skins

本主题解释如何将DevExpress主题/皮肤应用到应用程序中&#xff0c;如何允许用户在运行时在主题之间切换&#xff0c;如何自定义现有皮肤或创建自己的皮肤&#xff0c;等等。 WinForms订阅包括许多基本控件&#xff1a;按钮、复选框、表单、消息框、对话框、对话框等。 我们实现…

python面向过程与初始面向对象编程

让我们穿越到《龙珠》世界&#xff0c;一起揭开 面向对象编程 的神秘面纱吧。 面向过程编程与面向对象编程 天下第一武道会 选手登记 第 22 届天下第一武道会即将召开&#xff0c;各路武术高手齐聚一堂&#xff0c;其中最受瞩目的&#xff0c;当属卡卡罗特&#xff08;孙悟…

Docker高级篇之Docker搭建mysql主从复制架构

文章目录 1. 安装mysql主从复制2. 主从复制测试 1. 安装mysql主从复制 首先创建主节点 docker run -d -p 3308:3306 \ --privilegedtrue \ -v /Users/jackchai/Desktop/lottory_docker/learndocker/mymysql/master/log:/var/log/mysql \ -v /Users/jackchai/Desktop/lottory_…

dots_image 增强图像中的圆点特征

dots_image 增强图像中的圆点特征 1. dot_image 有什么用途&#xff1f;2. 点状字符的特征增强3. Halcon代码 1. dot_image 有什么用途&#xff1f; Enhance circular dots in an image. 这个算子可以增强图像中的圆点特征&#xff0c;例如下面的例子。 2. 点状字符的特征增强…

【数据结构与算法 | 二叉树篇】力扣101, 104, 111,LCR144

1. 力扣101 : 对称二叉树 (1). 题 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false…

知识图谱的应用---智慧政务

文章目录 智慧政务典型应用 智慧政务 智慧政务即通过“互联网政务服务”构建智慧型政府&#xff0c;利用云计算、移动物联网、人工智能、数据挖掘、知识管理等技术&#xff0c;提高政府在办公、监管、服务、决策中的智能水平&#xff0c;形成高效、敏捷、公开、便民的新型政府&…

微前端之旅:探索Qiankun的实践经验

theme: devui-blue 什么是微前端&#xff1f; 微前端是一种前端架构方法&#xff0c;它借鉴了微服务的架构理念&#xff0c;将一个庞大的前端应用拆分为多个独立灵活的小型应用&#xff0c;每个应用都可以独立开发、独立运行、独立部署&#xff0c;再将这些小型应用联合为一个完…

3D打印随形水路:模具水路的革命性技术

在快速发展的模具制造行业中&#xff0c;3D打印技术以其独特的优势正在引领一场技术革命。其中&#xff0c;3D打印随形水路技术&#xff0c;凭借其灵活性和定制化设计的能力&#xff0c;为模具带来了前所未有的变革。 模具3D打印随形水路技术&#xff0c;是一种利用3D打印技术制…

环 境 变 量

如果希望某一个文件在 CMD 窗口的任意路径下都可以打开&#xff0c;则需要将该文件的路径存放在环境变量中。 在 CMD 中运行该文件时&#xff0c;优先查看当前路径下的文件&#xff0c;如果没有找到&#xff0c;则进入环境变量中记录的路径下寻找该文件&#xff0c;如果能找到…

阿里通义千问,彻底爆了!(本地部署+实测)

点击“终码一生”&#xff0c;关注&#xff0c;置顶公众号 每日技术干货&#xff0c;第一时间送达&#xff01; 问大家一个问题&#xff1a;你是否想过在自己的电脑上部署一套大模型&#xff1f;并用自己的知识库训练他&#xff1f; 阿里通义千问今天发布了最新的开源大模型系…

灵动岛动效:打造沉浸式用户体验

灵动岛是专属于 iPhone 14 Pro 系列交互UI&#xff0c;通过通知消息的展示和状态的查看与硬件相结合&#xff0c;让 iPhone 14 Pro 系列的前置摄像头和传感器的“感叹号”&#xff0c;发生不同形状的变化。这样做的好处是让虚拟软件和硬件的交互变得更为流畅&#xff0c;以便让…

M1Pro 使用跳板机

Mac (M1 Pro) 通过Iterm2 使用跳板机 1、由于堡垒机&#xff08;跳板机&#xff09;不能支持mac系统终端工具&#xff0c;只支持xshell等win生态。所以我们需要先安装iterm2 装iterms教程 这里头对rz、sz的配置不详细。我们可以这样配置&#xff1a; where iterm2-send-zmod…

关闭文件及使用with语句

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 1 关闭文件 打开文件后&#xff0c;需要及时关闭&#xff0c;以免对文件造成不必要的破坏。关闭文件可以使用文件对象的close()方法实现。close()方…

网络安全实验BUAA-全套实验报告打包

下面是部分BUAA网络安全实验✅的实验内容 &#xff1a; 认识路由器、交换机。掌握路由器配置的基本指令。掌握正确配置路由器的方法&#xff0c;使网络正常工作。 本博客包括网络安全课程所有的实验报告&#xff1a;内容详细&#xff0c;一次下载打包 实验1-路由器配置实验2-AP…

Linux存储管理

简介 硬件上的存储设备目前有两类&#xff0c;通过磁头读写信息的机械硬盘和用主控芯片将信息写入晶体管的固态硬盘&#xff0c;硬盘调度算法等知识可以通过前面的操作系统设备管理文章学习&#xff0c;本章只介绍Linux中能对存储设备的操作。 为了让操作系统识别和管理物理磁…

SAP ERP系统主要模块简介

SAP系统通过提供一系列高度灵活的模块&#xff0c;满足企业在不同业务领域的需求。这些模块不仅功能齐全且相对独立&#xff0c;但它们之间又能紧密协作&#xff0c;共同构筑一个协同高效的工作环境。 财务会计&#xff08;FI&#xff09;模块 它涵盖了总账、应收账款、应付账…