【源码那些事】超详细的ArrayList底层源码+经典面试题

本文详细介绍了ArrayList的构造方法、基础操作,包括添加、删除、修改元素,以及克隆和扩容机制。文章还探讨了ArrayList的性能优化,如预设容量以避免频繁扩容,以及在多线程环境下的使用注意事项。此外,对比了ArrayList与LinkedList在随机访问和插入删除操作上的性能差异,并分析了ArrayList的线程安全性问题。

🙏相见即是有缘,如果对你有帮助,给博主一个免费的点赞以示鼓励把QAQ☺

在这里插入图片描述

👋ArrayList底层源码

ArrayList的类图示:
在这里插入图片描述

  1. ArrayList集合介绍
    1. List底层基于动态数组实现
  2. 数组结构介绍
    • 增删慢:每次删除元素都需要更改数组长度,拷贝以及移动元素位置
    • 查询快:由于数组在内存中是一块连续空间,因此可以根据地址+索引的方式快速获取对应位置上的元素

👋1 ArrayList继承关系

Serializable标记性接口

介绍 类的序列化由实现java.io.Serializable接口的类启用。不实现此接口的类将不会使任何状态序列化反序列化。可序列化类的所有子类型都是可序列化的,序列化接口没有方法或字段,仅用于标识可串行化的语义

  1. 序列化:将对象的数据写入到文件(写对象)
  2. 反序列化:将文件中对象的数据读取出来(读对象)

Cloneable标记性接口

  1. 介绍 一个类实现Cloneable接口来指示Object.clone()方法,该方法对于该类的实例进行字段的复制是合法的。在不实现Cloneable接口的实例上调用对象的克隆方法会导致异常CloneNotSupportedException被抛出。简言之:克隆就是依据已有的数据,创造一份新的完全一样的数据拷贝
  2. 克隆的前提条件
    • 被克隆对象所在的类必须实现Cloneable接口
    • 必须重写clone()方法

Serializable接口

  1. 由List实现使用,以表明它们支持快速(通常为恒定时间)随机访问
  2. 此接口的主要目的是允许算法更改其行为,以便在应用于随机访问列表或顺序访问列表时提供良好的性能
    在这里插入图片描述

👋1.1 Cloneable的底层实现

    /**
     * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this <tt>ArrayList</tt> instance
     */
    public Object clone() {
   
   
        try {
   
   
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
   
   
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
可以看到通过调用clone()方法,返回了一个ArrayList对象
 v.elementData = Arrays.copyOf(elementData, size);

是我们能完成复制的关键,点进去

    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 Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

我们原来数组中的数据通过System.arraycopy方法被拷贝到copy数组中,并将这个数组返回

克隆牵扯到浅拷贝和深拷贝,这里简述一下浅拷贝深拷贝的区别

首先创建一个Student类

public class Student implements Cloneable {
   
   
    private String name;
    private Integer age;
    public Student() {
   
   
    }
    public Student(String name, Integer age) {
   
   
        this.name = name;
        this.age = age;
    }
    public String getName() {
   
   
        return name;
    }
    public void setName(String name) {
   
   
        this.name = name;
    }
    public Integer getAge() {
   
   
        return age;
    }
    public void setAge(Integer age) {
   
   
        this.age = age;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
   
   
        return super.clone();
    }
    @Override
    public String toString() {
   
   
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

浅拷贝实例

package ArrayListProject.CloneTest;

import java.util.ArrayList;
import java.util.List;

/**
 * Description
 * User:
 * Date:
 * Time:
 */
public class Test {
   
   
    public static void main(String[] args) {
   
   
        ArrayList list = new ArrayList();
        Student stu = new Student("zs",18);
        list.add("123");
        list.add(stu);
        System.out.println("未拷贝之前的数组:"+list);
        ArrayList clone = (ArrayList)list.clone();
        System.out.println("拷贝之后的数组:"+clone);
        System.out.println("两个数组地址是否相等:"+(list==clone));
        System.out.println("两个数组中的stu对象是否相等:");
        System.out.println("list中的stu对象:"+list.get(1));
        System.out.println("修改stu对象之前,clone中的stu对象:"+clone.get(1));
        Student stu1 = (Student)list.get(1);
        stu1.setName("ls");
        System.out.println("修改stu对象之后,clone中的stu对象:"+clone.get(1));
        System.out.println("两者stu对象的地址是否相同"+(list.get(1)==clone.get(1)));
    }
}

未拷贝之前的数组:[123, Student{
   
   name='zs', age=18}]
拷贝之后的数组:[123, Student{
   
   name='zs', age=18}]
两个数组地址是否相等:false
两个数组中的stu对象是否相等:
list中的stu对象:Student{
   
   name='zs', age=18}
修改stu对象之前,clone中的stu对象:Student{
   
   name='zs', age=18}
修改stu对象之后,clone中的stu对象:Student{
   
   name='ls', age=18}
两者stu对象的地址是否相同true

由此我们得出

  • 克隆之后的对象是一个新对象,list==clone为false可知二者地址不相同
  • 由list.get(1)==clone.get(1)为true可知,两者中的stu对象为同一对象
  • 修改list中的stu对象,clone中的stu对象也会随之改变可知,克隆的是stu对象的地址,并没有创建新的对象,它仅仅是拷贝了一份引用地址
  • 基本数据类型可以达到完全复制,而引用数据类型则不可以

深拷贝和浅拷贝不同,深拷贝中拷贝的对象是一个完全新的对象,他拷贝的并不是引用地址,而是实实在在的创建了一个对象

在这里插入图片描述

👋1.2 RandomAccess标记接口

  • 由List实现使用,以表明它们支持快速(通常为恒定时间)随机访问
  • 此接口的主要目的是允许算法更改其行为,以便在应用于随机访问列表或顺序访问列表时提供良好的性能

因此List实现RandomAccess在执行随机访问时,性能会比顺序访问更快

public interface RandomAccess {
   
   
}

我们通过一个案例来证明

我们以1000000个数据作为测试

package ArrayListProject.CloneTest;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test02 {
   
   
    public static void main(String[] args) {
   
   
        //创建ArrayList集合
        List<String> list = new ArrayList<>();
        //添加100W条数据
        for (int i = 0; i < 1000000; i++) {
   
   
            list.add(i+"a");
        }

        //测试随机访问时间
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
   
   
            //取出集合的每一个
评论 87
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

温文艾尔

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

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

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

打赏作者

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

抵扣说明:

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

余额充值