前言:
本篇文章具体探讨 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的下标
4)getPos()
获取pos位置元素
5)size()
链表长度
6)toRemove()
删除第一次出现的关键词
- 调用成员方法search
- 向前挪(i = index;i
- arr[usedSize] = null,this.useSized–
7)clear()
链表清除
3 ArrayList 类
3.1 ArrayList的继承关系
ArrayList 是一个集合类, 除了上述提到的继承自 List 以外,还有诸多复杂的继承关系,下面只是简单提及
3.2 成员变量
- int size
- Object[] elementData
- Object[] EMPTY_ELEMENTDATA = {}
- 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) 方法进行增容
数组元素较少(远小于 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 所以可以使用迭代器进行打印
文章至此就结束啦,如果看官朋友觉得还不错,小编求 点赞、评论、收藏 三连,十分感谢