细说 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<this.useSized-1)
  • 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 所以可以使用迭代器进行打印

  • 遍历集合中的元素

请添加图片描述



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

  • 40
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 39
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 39
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值