ArrayList 知识点

一、ArrayList概述

ArrayList是Java集合框架中的一个类,它继承了AbstractList类,并且实现了List接口,是一个动态数组,数组长度可以动态增加和缩小,它可以对数组中的元素进行增、删、改、查等操作,还可以存储任意类型的对象。与传统数组相比,ArrayList的容量能动态增长,提供了更便捷的操作接口和灵活的扩展能力。

二、ArrayList特点

  1. 动态数组:支持动态扩容,使用者无需关心底层数据结构的变化,封装性好。
  2. 有序存储:存储的元素可重复,并支持null元素的存储,且维护插入顺序。
  3. 底层为数组:查找快,时间复杂度为O(1);增删慢,尤其是在列表中间或头部操作时,需要移动大量元素,时间复杂度为O(n)。
  4. 线程不安全:不支持同步,一般建议在单线程中使用ArrayList,在多线程环境下可选择Vector或者CopyOnWriteArrayList。

三、ArrayList继承体系

ArrayList继承AbstractList抽象父类,实现了List、RandomAccess、Cloneable、Serializable接口。

  1. List接口:定义了List的一些操作规范。
  2. RandomAccess接口:是一个空接口,代表可随机访问。实现了此接口的集合使用for循环遍历速度更快。
  3. Cloneable接口:空接口,实现此接口是为了可以调用clone()方法,ArrayList的clone()方法属于浅克隆。
  4. Serializable接口:空接口,代表可序列化。

四、重要属性

  1. transient Object[] elementData:这是ArrayList的底层,是一个object数组。由transient修饰,代表此数组不参与序列化,而是使用另外的序列化方式(使用writeObject方法进行序列化,只序列化有值的位置,其他未赋值的位置不进行序列化,节省空间)。
  2. private int size:指集合包含的元素数量,注意与elementData.length(集合容量)的区别。

五、构造方法

ArrayList有三个构造方法:

  1. 无参构造方法
    • JDK 8之前:使用无参构造方法创建ArrayList对象时,数组的默认长度为10。例如在JDK 7中,ArrayList空参的构造器会调用本类当中重载的带一个int整型参数指定容量的构造器,将参数10传进去,对当前底层数组就进行了初始化,底层创建了一个长度为10的Object[]类型数组。
    public ArrayList() {  this.elementData = new Object[10]; }
    • JDK 8及之后:使用无参构造方法创建ArrayList对象时,数组的默认长度为0。在第一次添加元素时,才开始按照10进行扩容。
    public ArrayList() {  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
  2. 指定初始容量的构造方法:创建一个具有指定初始容量的空ArrayList。
ArrayList<String> list2 = new ArrayList<>(20);
  1. 包含指定集合的构造方法:创建一个包含指定集合元素的ArrayList,初始容量为集合大小。
import java.util.Arrays; import java.util.Collection; import java.util.ArrayList;  Collection<String> collection = Arrays.asList("元素1", "元素2"); ArrayList<String> list3 = new ArrayList<>(collection);

六、常用方法

1. 添加元素
  • add(E e):将指定的元素添加到列表的末尾。
list1.add("元素1");
  • add(int index, E element):在指定的位置插入指定的元素。
list1.add(1, "元素2");
  • addAll(Collection<? extends E> c):按照指定collection的迭代器所返回的元素顺序,将该collection中的所有元素添加到此列表的末尾。
Collection<String> collection = Arrays.asList("元素3", "元素4"); list1.addAll(collection);
  • addAll(int index, Collection<? extends E> c):从指定的位置开始,将指定collection中的所有元素插入到列表中。
list1.addAll(2, collection);
2. 获取元素
  • get(int index):返回列表中指定位置的元素。
String element = list1.get(0);
3. 修改元素
  • set(int index, E element):用指定的元素替换列表中指定位置的元素。
list1.set(0, "新元素");
4. 删除元素
  • remove(int index):移除列表中指定位置的元素。
list1.remove(1);
  • remove(Object o):移除列表中第一个出现的指定元素(如果存在)。
list1.remove("元素2");
  • removeAll(Collection<?> c):移除列表中所有包含在指定集合中的元素。
list1.removeAll(collection);
  • retainAll(Collection<?> c):仅保留列表中包含在指定集合中的元素。
list1.retainAll(collection);
  • clear():移除列表中的所有元素。
list1.clear();
5. 查询元素
  • contains(Object o):判断列表中是否包含指定的元素。
boolean exists = list1.contains("元素1");
  • indexOf(Object o):返回列表中第一次出现的指定元素的索引,如果未找到则返回 -1。
int index = list1.indexOf("元素1");
  • lastIndexOf(Object o):返回列表中最后一次出现的指定元素的索引,如果未找到则返回 -1。
int lastIndex = list1.lastIndexOf("元素1");
  • isEmpty():判断列表是否为空。
boolean empty = list1.isEmpty();
  • size():返回列表中元素的数量。
int size = list1.size();
6. 其他常用方法
  • subList(int fromIndex, int toIndex):返回列表中从fromIndex到toIndex(不包括toIndex)的子列表。
import java.util.List; List<String> subList = list1.subList(1, 3);
  • toArray():返回一个包含列表中所有元素的数组。
Object[] array = list1.toArray();
  • toArray(T[] a):返回一个包含列表中所有元素的数组,数组类型为指定类型。
String[] array = list1.toArray(new String[0]);

七、遍历方式

  1. 使用增强型for循环
for (String element : list1) {  System.out.println(element); }
  1. 使用Iterator
import java.util.Iterator; Iterator<String> iterator = list1.iterator(); while (iterator.hasNext()) {  String element = iterator.next();  System.out.println(element); }
  1. 使用forEach方法(Java 8+)
list1.forEach(element -> System.out.println(element));

八、动态扩容机制

1. 扩容触发条件

当调用add()方法时,若当前数组容量(elementData.length)小于size + 1,则触发扩容。

2. 扩容规则

新容量计算:newCapacity = oldCapacity + (oldCapacity >> 1)(即原容量的1.5倍)。特殊情况处理:若计算后的容量仍小于所需最小容量(minCapacity),则直接使用minCapacity;若扩容过程中出现整数溢出(极端大容量),抛出OutOfMemoryError。

3. 示例

假设初始情况下,ArrayList的底层数组容量为10,并且向其中添加了11个元素。当尝试再次添加元素时,容量不足,ArrayList会自动进行扩容操作。根据ensureCapacityInternal()方法,计算出自动扩容后最小容量为11,进行扩容,根据grow()方法,得到新容量为15(10 + 10 >> 1),并将旧数组的元素复制到新数组中。

九、使用场景

  1. 频繁读取数据:例如需要根据索引快速访问元素,ArrayList通过索引访问元素的时间复杂度为O(1),适合此类场景。
  2. 数据量较小或变化不大:避免频繁扩容带来的性能开销。
  3. 需要节省内存:ArrayList的内存占用比LinkedList更少。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值