• 细说 List、顺序表、ArrayList类(附 add 源码解读)—— 数据结构


    请添加图片描述

    前言:
    本篇文章具体探讨 List、顺序表 和 ArrayList 相关概念及常用常用方法,同时对 ArrayList 中 offer 源码进行解读,理解 ArrayList 中的扩容机制,希望能对各位看官朋友有所裨益,如有问题欢迎批评指正。

    重点:

    • List 常用方法
    • 顺序表的基本概念和模拟实现
    • ArrayList add源码及其他常用方法



    1 List

    讲顺序表之前我们先来看看 List 接口 同其他数据集合的基本关系,以及常用的成员方法

    1.1 是什么

    • List 是一个 线性表,逻辑上是连续存储的数据集合, 该数据集合中包含了相同类型元素的有限序列,在该数据集合中多执行增删查改的操作
      • 我们后续所分享的 ArrayList 就是一个实现List 接口的规范蓝本
    • List 接口 继承自 Collection 接口,Collection 中规范了后续的数据集合的一些常用方法
      • Collection 也继承自迭代器 Iterable
    • List 是一个接口,具体使用过程要创建 实现了 List 接口的类实例化对象
      • 在刷题过程中常见于 List 作为返回值类型,一般都是通过 创建 ArrayList 和 LinkedList 对象向上转型 使用 List

    1.2 常用方法

    List 有很多方法,但在具体的刷题和实践过程中常用的方法有如下几种:

    方法名具体功用
    boolean add (E e)尾插 e 元素
    void add (int index, E element)插入 e 元素到index 位置
    boolean addAll (Collection c)尾插 Collection 集合元素到 list 中
    E remove (int index)删除 index 位置元素
    boolean remove (Object o)删除 第一个o 元素
    void clear ()清空 list
    E get (int index)获取 index 位置元素
    boolean contains (Object o)判断 list 中是否包含 o 元素
    int indexOf (Object o)返回 第一个 o 元素下表
    int lastIndexOf (Object o)返回 最后一个 o 元素下表
    ist subList (int fromIndex, int toIndex)截取 下标从form 到 to 的元素



    2 顺序表

    在具体面试问题中要区分指的是 顺序表 还是 ArrayList,这两个概念上略有不同

    2.1 顺序表 和 线性表

    线性表:n个具有相同特性的数据元素的有限序列

    线性表仅仅是在逻辑上是线性结构,在物理上可能不是线性结构(链表),常用 数组 和 链式 两种结构进行储存

    • 顺序表是线性表的一种表现形式,使用 数组结构 进行储存, 在物理地址上连续的存储单元进行元素存储的线性结构
    • 顺序表分为 静态顺序表 和 动态顺序表
      • 静态顺序表 表示定长数组,数组长度固定
      • 动态顺序表 表示动态开辟的数组储存

    2.2 顺序表常用接口的模拟实现

    • 成员方法
      • elem useSized intCapacity
      • 都用private修饰, intCapacity使用 privat static final修饰
    • 构造方法
      • 构造方法:this.elem[] = new int[intCapacity];

    1)add()

    pos元素位置增加

    • 判满(array.copyOf(this.elem, 2*this.elem.length)), 判空
    • 向后挪动(起始i = usedSized - 1,i>=pos ,i–),
    • 放置元素
      请添加图片描述

    2)display()

    打印元素

    • 遍历数组进行打印

    请添加图片描述

    3)search()

    元素toFind的下标

    • 遍历数组,找不到返回-1

    请添加图片描述


    4)getPos()

    获取pos位置元素

    • 先判空 和 pos位置合法性

    请添加图片描述

    5)size()

    链表长度

    请添加图片描述

    6)toRemove()

    删除第一次出现的关键词

    • 调用成员方法search
    • 向前挪(i = index;i
    • arr[usedSize] = null,this.useSized–

    请添加图片描述

    7)clear()

    链表清除

    • 直接this.usedSized = 0;

    请添加图片描述





    3 ArrayList 类

    3.1 ArrayList的继承关系

    ArrayList 是一个集合类, 除了上述提到的继承自 List 以外,还有诸多复杂的继承关系,下面只是简单提及

    请添加图片描述

    3.2 成员变量

    • int size
      • 数组中包含元素的数量
    • Object[] elementData
      • elementData 即是当前数组的引用
    • Object[] EMPTY_ELEMENTDATA = {}
      • 用于构造指定容量为 0 的数组
    • Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA
      • 用于不带参数的构造方法,构造指定容量为 0 的数组
    • int DEFAULT_CAPACITY
      • 默认大小,(用于不带参数的构造)用于第一次 add

    3.3 构造方法

    1) 不带参数的构造方法

    • 将 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 静态成员变量赋值给当前数组的引用
    • 默认设置数组大小为 0,当调用 add 方法数组大小变为 10,数组满了以后 1.5倍 增大

    请添加图片描述

    2 ) 接收其他集合类

    • 将传入的集合类中的元素拷贝到当前数组中,elementData 进行接收

    请添加图片描述

    3) 指定容量

    • 指定容量 > 0,创建 initialCapacity 大小的数组
    • 制定容量 == 0,创建空数组,将 EMPTY_ELEMENTDATA 静态成员变量赋值给当前数组的引用

    请添加图片描述

    3.4 成员方法

    请添加图片描述

    1)add(E e)

    (1)ensureCapacityInternal 尝试进行增容

    • 参数:传入 size + 1 表示 minCapacity,因为要判断增加一个元素之后是否超出数组长度
    • 内部:只有一个方法 ensureExplicitCapacity
      • ensureExplicitCapacity方法传入参数为 calculateCapacity(elementData, minCapacity)
      • calculateCapacity 中判断是否是通过不带参数的构造方法创建的对象
        • 是,返回 DEFAULT_CAPACITY(10) 和 minCapacity(size + 1) 最大值
        • 否,返回 minCapacity(size + 1)

    请添加图片描述
    请添加图片描述

    • ensureExplicitCapacity方法 具体实现
      • 判断 minCapacity 是否大于 数组长度(增加一个元素之后是否超出数组长度)
        • 是,调用 grow(minCapacity) 方法进行增容

    请添加图片描述

    • grow(int minCapacity) 方法

    数组元素较少(远小于 Interger 最大值 ),进行的是1.5 倍增容

    数组元素很多,就要考虑是否溢出的问题

    • newCapacity = 1. 5 * elementData.length,
    • 数组中元素较少时,进行的是1.5 倍增容
    • 数组中元素较多时,判断如果 newCapacity 大于 Interger 最大值,则不进行增容,继续向后判断
    • 数组中元素较多时,判断如果 newCapacity 大于 Interger 最大值 - 8, 进行 hugeCapacity(minCapacity)
      • hugeCapacity 方法实际上就是当你 1.5倍增容结果 > Interger 最大值 - 8,即元素可能要溢出时,进行一系列的判断调整,最多再进行一次增容
      • hugeCapacity 中如果原本 size = Interger 最大值,现在size + 1 就会溢出,所以直接抛出异常
      • hugeCapacity 中如果 size + 1 大于 Interger 最大值 - 8,返回 Interger 最大值;否则返回 Interger 最大值 - 8(也就是1.5倍增容成功后才大于 Interger 最大值 - 8)
      • hugeCapacity 这种做法其他数据集合中也有所使用,比如优先级队列中的 offer 成员方法(详解可以查看我上一篇关于 TopK 的内容)

    请添加图片描述
    请添加图片描述


    (2)未抛出异常 尝试增容结束

    • 直接尾插元素 elementData[size++] = e

    请添加图片描述

    (3)总结

    • 不带参数的构造方法创建的对象时,数组大小为0,当进行第一次插入元素 数组大小变为 10
    • 在插入元素而有必要进行扩容时,先判断 1.5 倍扩容是否合适,
      • 扩容结果可能非常大,对可能会溢出的扩容做出反应(可能会直接抛出异常),进行做出反应后扩容
      • 扩容结果合适( 远小于Interger最大值 ),直接进行1.5倍扩容

    2)其他常用方法

    方法具体功用
    void add (int index, E element)添加 元素 e到 index 位置
    boolean addAll (Collection c)添加 c 集合中的元素到末尾
    E remove (int index)删除 index位置的元素,返回该元素 e
    boolean remove (Object o)删除 指定的第一个 o 元素
    E get (int index)获取 index 位置的元素
    E set (int index, E element)设置 index 位置元素为 e
    void clear ()清除 所有元素
    boolean contains (Object o)查看 是否包含了元素 o
    int indexOf (Object o)获取 第一个 o 元素的下标
    int lastIndexOf (Object o)获取 最后一个 o 元素的下标
    List subList (int fromIndex, int toIndex)截取 从 from 到 to位置的所有元素,返回 List

    tip: Object[] toArray()

    • 以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组
    • 返回值Object[],不能强转数组类型,用具体类型数组接收

    3.5 打印 ArrayList 对象

    1)iterator

    前面我们介绍过 ArrayList 继承了 Iterator 所以可以使用迭代器进行打印

    • 遍历集合中的元素

    请添加图片描述



    文章至此就结束啦,如果看官朋友觉得还不错,小编求 点赞、评论、收藏 三连,十分感谢

  • 相关阅读:
    8-事件组或标志
    Acwing—数据结构
    idea 模板参数注释 {@link}
    【2023】Git版本控制-远程仓库详解
    【MFC】tab控件 仿任务管理器 枚举窗口和进程
    少走点弯路:Wiki.js 通过 Generic OAuth2 进行身份验证
    java-net-php-python-s2s酒店管理系统计算机毕业设计程序
    自动化测试与Jenkins持续集成实战
    德国金融监管机构网站遭遇大规模DDoS攻击后“瘫痪”
    字符串最大跨距
  • 原文地址:https://blog.csdn.net/honglan297/article/details/125886047