Java ArrayList底层原理详情笔记和相关面试题

ArrayList底层原理ArrayList集合底层数据结构ArrayList集合介绍数组结构介绍ArrayList继承关系实现Serializable标记型接口实现Cloneable标记型接口实现RandomAccess标记型接口AbstractList抽象类ArrayList源码分析相关变量构造方法添加方法修改方法获取元素删除元素清空方法迭代器并发修改异常产生的原因结论:面试题ArrayList是如何扩容的?ArrayList频繁扩容导致添加性能急剧下降,如何处理?ArrayList插入或删除元素一定比
摘要由CSDN通过智能技术生成

ArrayList集合底层数据结构

ArrayList集合介绍

实现List接口,底层可调整大小的数组实现

数组:一旦初始化长度就不可以改变

数组结构介绍

增删慢:每次删除元素,都需要更改数组长度、拷贝以及移动元素的位置

查询快:由于数组在内存中是一块连续空间,因此可以根据地址+索引的方式快速获取对应位置上的元素

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OgDnqbOF-1641732652336)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220103113829200.png)]

ArrayList继承关系

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
   

实现Serializable标记型接口

序列化:将对象转化为字节数组的过程

反序列化:将字节数组转化为对象的过程

实现Cloneable标记型接口

克隆:将ArrayList集合的数组clone到另一个集合

实际底层调用的是Object中的clone()方法,而clone是一个native方法

Object o=list.clone();
System.out.println(o==list);//地址不一样,深度克隆

使用了Arrays.copyOf()方法进行复制

ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);

浅拷贝:基本数据类型可以达到完全复制,引用数据类型拷贝的是栈上的地址值,所以修改引用类型的数据,会改变原来的数据

深拷贝:基本数据类型和引用类型都可以完全复制,引用对象在堆中创建新的对象,对原数据没有任何影响

深拷贝的实现方式:

​ 实现Cloneable接口并重写clone()方法

​ 让其clone()方法通过序列化和反序列化的方式来生成一个原对象的深拷贝副本

实现RandomAccess标记型接口

ArrayList支持随机访问

通过get(i)即可获得相应内存中存放的值。原因是因为ArrayList存放的内容在内存中是连续的,数组直接用[ ]访问,相当于直接操作内存地址,所以随机访问的效率较高。

普通的for循环是随机访问的,所以遍历ArrayList使用普通for循环比增强for循环和迭代器的效率高。

而LinkedList是一个双向链表,链表只能顺序访问,不支持随机访问,LinkedList中的get方法是按照顺序从列表的一端开始检查,直到找到要找的地址。所以遍历LinkedList使用增强for循环和迭代器的效率高,使用普通for循环会每次都从头开始遍历,效率较差。

AbstractList抽象类

AbstractList 虽然是抽象类,但其内部只有一个抽象方法 get():

abstract public E get(int index);
从字面上看这是获取的方法,子类必须实现它,一般是作为获取元素的用途,除此之外,如果子类要操作元素,还需要重写 add(), set(), remove() 方法,因为 AbstractList 虽然定义了这几个方法,但默认是不支持的,

ArrayList源码分析

相关变量

//初始容量为10
private static final int DEFAULT_CAPACITY = 10;
//长度为0的空数组
private static final Object[] EMPTY_ELEMENTDATA = {
   };
//默认为长度为0的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
   };
//集合真正存元素的数组
transient Object[] elementData; // non-private to simplify nested class access
//集合的长度
private int size;

构造方法

Constructor 描述
ArrayList() 构造一个初始容量为十的空列表
ArrayList(int initialCapacity) 构造具有指定初始容量的空列表
ArrayList(Collection <? extends E>c) 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序

空参:ArrayList()

public ArrayList() {
   
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

实际上空参的数组长度为0

有参:ArrayList(int initialCapacity)

public ArrayList(int initialCapacity) {
   //手动初始化
    if (initialCapacity > 0) {
   
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
   
        //如果长度为0,使用final修饰的空数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
   
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

如果在使用ArrayList集合,知道长度就尽量给定长度,减少内存的消耗,因为数组每次扩容,都会复制一份新的数组,会极大的消耗内存

有参:ArrayList(Collection <? extends E>c)

public ArrayList(Collection<? extends E> c) {
   
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
   
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
   
        //如果数组长度为0,使用的是默认的空数组
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

进入到Arrays.copyOf方法

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
   
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

抹泪的知更鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值