深度解析ArrayList使用

2年前基础语言38552
深度解析ArrayList使用 熬夜磕代码丶 已于2022-09-21 20:45:56修改 8572 收藏 95 分类专栏: 数据结构与算法 JAVA SE 文章标签: java 数据结构 前端 jvm 于2022-09-19 08:54:55首次发布 数据结构与算法 同时被 2 个专栏收录 17 篇文章 22 订阅 订阅专栏 JAVA SE 23 篇文章 28 订阅 订阅专栏

文章目录 一、ArrayList是什么?二、ArrayList的构造方法三、ArrayList的常见方法常见方法遍历ArrayList使用迭代器删除元素 四、ArrayList的扩容机制

一、ArrayList是什么?

ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。ArrayList 继承了 AbstractList ,并实现了 List 接口。

我们发现ArrayList类位于java.util包中,使用之前需要去引入它.

import java.util.ArrayList;

ArrayList如何定义:

ArrayList<E> arrayList = new ArrayList<E>();

此数E为泛型实参类型,只能为引用数据类型,ArrayList想存储什么类型的数据,我们就传什么就可以。

List<E> list = new ArrayList<E>();

因我们的ArrayList是继承于List的,所以我们可以用父类去接受子类对象,向上转型。

二、ArrayList的构造方法

我们发现ArrayList一共提供了三种构造方法。 构造方法1: 构造一个空的顺序表

List<Integer> list = new ArrayList<>();

构造方法2: 构造一个指定大小为5的顺序表

List<Integer> list = new ArrayList<>(5);

构造方法3: 此处传入的要么是自己本身类型,或者是子类类型。 此处构造一个和list2元素一致的顺序表

List<Integer> list2 = new ArrayList<>(5); List<Integer> list = new ArrayList<>(list2);

初始时的坑

List list = new ArrayList();

这样对顺序表进行初始化的话,没有指定任何类型,这样的话可以存放任意类型的元素,这样会存在很大的安全隐患。

三、ArrayList的常见方法 常见方法

在我的上一篇实现顺序表中,已经把大多数方法手动实现了一遍,此处我在说明几个重点的。 remove方法 ArrayList提供给我们两个remove方法 一个是传入要删除的下标,一个是要删除的引用。

public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(10); list.add(8); list.remove(1); System.out.println(list); }

如果我们传入的是int类型那么我们删除的是该下标的数据。

public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(10); list.add(8); list.remove(new Integer(10)); System.out.println(list); }

如果我们传入的是引用数据类型,那么将删除该顺序表中的该引用数据。 subList方法

public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); list.add(4); System.out.println("截取之前的list:"+list); List<Integer> list1 = list.subList(1,3); list1.set(0,5); System.out.println("截取之后的list:"+list); }

为啥改了list1之后,list的数据也发生了变化。 因为在截取之后并没有复制一份内容给list1,而是指向同一块内容。

遍历ArrayList

方法1:

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

方法2:

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

方法3: 使用迭代器

方法作用.next()返回迭代器写一个元素,更新迭代器状态.hasNext()检测集合中是否还有元素.remove()将迭代器返回的元素删除 public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); list.add(4); Iterator<Integer> it = list.listIterator(); while (it.hasNext()) { System.out.print(it.next()+" "); } System.out.println(); } 使用迭代器删除元素

我们删除集合小于3的元素

public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); list.add(4); Iterator<Integer> it = list.listIterator(); while (it.hasNext()) { if(it.next() < 3) { it.remove(); } } System.out.println(list); }

迭代器的remove方法只能删除当前指向的元素。

四、ArrayList的扩容机制 List<Integer> list = new ArrayList<>();

很多资料都在说这样初始化一个集合,说默认大小为10,那事实到底是不是如此呢?我们一步步查看源码一探究竟。

我们发现当前引用指向后面那个引用 我们发现这里的引用是一个空引用,初始集合时,是一个空集合。 那如何扩容呢? 既然我们的顺序表是空的,那如何add呢? 如果是无参构造的话,就传一个默认大小和传入大小的最大值。 我们可以发现如果大于数组大小时,就会grow。 我们可以发现计算一个新容量,和我们传入的大小做比较,如果新容量小于传入的大小时,指定当前集合大小为传入的大小。否则,对集合大小进行1.5倍扩容。 但如果我们1.5倍扩容的大小超过了这个指定的范围2147483639时 系统会报一个内存溢出异常。

1. 检测是否真正需要扩容,如果是调用grow准备扩容 2. 预估需要库容的大小 初步预估按照1.5倍大小扩容 如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败 3. 使用copyOf进行扩容

相关文章

数字IC、FPGA基础知识整理

数字IC、FPGA基础知识整理...

滤波笔记一:卡尔曼滤波(Kalman Filtering)详解

滤波笔记一:卡尔曼滤波(Kalman Filtering)详解...

AI画图 Disco-diffusion 本地搭建测试

AI画图 Disco-diffusion 本地搭建测试...

在本机搭建自己的ftp服务器--最简单的方法(详细教程)

在本机搭建自己的ftp服务器--最简单的方法(详细教程)...

机器学习强基计划4-4:详解半朴素贝叶斯分类AODE原理(附Python实现)

机器学习强基计划4-4:详解半朴素贝叶斯分类AODE原理(附Python实现)...

Rabbitmq死信队列

Rabbitmq死信队列...