ArrayList

概念:

        ArrayList是Java集合框架中的一个类,它继承于AbstractList, 实现了List接口。是一个动态数组(空间大小可变),可以存储任意类型的对象。 实现了RandomAccess接口 ,可以对元素进行快速访问。 实现了Serializable接口,说明ArrayList可以被序列化,还有Cloneable接口,可以被复制。

 

特点:

  1. 容量的可扩展性:空间大小是可以动态增长的,它会根据需要自动调整内部数组的大小。

  2. 随机索引访问:由于ArrayList是基于数组实现的,因此可以通过索引直接访问元素,提供了良好的随机访问性能。

  3. 可变性:ArrayList支持添加、删除和修改元素,可以根据需要对列表进行动态修改。

  4. 有序性:元素在ArrayList中的顺序是有序的,就像在数组中一样。可以根据索引来获取和操作元素。

  5. 允许重复元素:ArrayList允许存储重复的元素。

  6. 线程不安全

  7. 可以存储任意引用类型(Object)的对象, 并且存储的必须是对象(引用类型),不能存储基础数据类型,基本数据类型通过装箱为包装类数据进行存储。。

源码分析:

因为源码语句很多,这里只分析常用方法的方法代码(通过测试类中的方法语句ctrl+鼠标左键查看源码)

测试用例:

import java.util.*;
/**
 * @author felix
 * @created 2023/7/19
 */
public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> wc = new ArrayList<>();//无参构造
        ArrayList<Integer> yc = new ArrayList<>(6); //有参构造
        wc.add(1);//添加元素
        wc.add(2);
        wc.add(2);
//        System.out.println(wc);
        yc.add(5);
        yc.add(6);
        wc.add(0,3);//在指定位置插入
//        System.out.println(wc);
        wc.addAll(yc);//将指定 集合 中的所有元素添加到此列表的尾部。
        wc.addAll(0,yc);//指定的位置开始,将指定 集合 中的所有元素插入到此列表中。
//        System.out.println(wc);
        wc.set(1,9);//用指定的元素替代此列表中指定位置上的元素。
//        System.out.println(wc);
        wc.remove(1); //移除此列表中指定位置上的元素。
        wc.remove(Integer.valueOf(1)); //移除列表中首次出现的指定元素(如果存在)
//        System.out.println(wc);
        wc.removeAll(yc);
        wc.get(0);
        System.out.println(wc.size());;//打印空间大小
​
    }
}

ArrayList的底层实现

ArrayList底层采用Java数组来存储集合中的内容,这个数组是Object类型,它的操作基本上都是基于对数组的操作。

transient Object[] elementData; //ArrayList的底层实现
    //访问级别为包内私有,使内部类能够访问到其中的元素
    //transient 是java关键字,为变量修饰符。
    //用transient声明一个实例变量,可以关闭关闭serialization持久化对象实例的机制,避免对象被序列化。这样,当对象存储时,它的值不需要持久化。
    //Object[] elementData 是ArrayList容器,ArrayList的基本操作都是基于elementData变量来进行的。

一些静态常量

//一个序列化版本号,用于控制Java序列化和反序列化的过程中的版本兼容性
private static final long serialVersionUID = 8683452581122892189L;
​
//数组默认初始容量
private static final int DEFAULT_CAPACITY = 10;
​
//为了对应不同的构造函数(有参构造、无参构造),ArrayList使用了不同的数组。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //提供给无参构造函数使用的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
    //提供给有参构造函数使用的空数组
​
//数组最大容量值 
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造函数

无参构造:
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //调用无参空数组初始化 elementData对象
}
有参构造:
public ArrayList(int initialCapacity) {// initialCapacity数组容量
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;//调用有参空数组初始化 elementData对象
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
}
集合构造的构造函数:
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 {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

add()方法

//直接在列表末尾添加
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  //保证内部容量
    // Increments modCount!! 每次增加操作后modCount都会递增
    elementData[size++] = e;//在数组末尾赋值
    return true; //是否成功
}
//modCount是一个在Java集合框架中常见的属性,用于追踪集合的结构性修改次数。它用于在迭代器遍历中检测并发修改异常
​
//在指定下标位置添加
public void add(int index, E element) {
    rangeCheckForAdd(index); //对添加操作进行索引范围检查,确保在执行添加操作时,所指定的索引处于有效的范围内
    ensureCapacityInternal(size + 1);  //保证内部容量--扩容
    System.arraycopy(elementData, index,
                     elementData, index + 1,size - index); //数组拷贝,将原本的elementData中的index下标位置后的所有元素复制,在index+1位置放入复制的内容。
    //数组拷贝后再将index下标位置的元素的值替换为element,就完成了插入
    elementData[index] = element;
    size++;//这里的size只是记录列表长度
}
​
//rangeCheckForAdd 
private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)//判断索引是否在size范围内,不在就抛出异常
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
​
//System.arraycopy  在不同数组之间进行元素复制。
 public static native void arraycopy(Object src,  int  srcPos,
                                  Object dest, int destPos,int length);
//src:源数组,即要复制元素的数组。
//srcPos:源数组的起始位置,从该位置开始复制元素。
//dest:目标数组,即要将元素复制到的数组。
//destPos:目标数组的起始位置,从该位置开始粘贴复制的元素。
//length:要复制的元素数量。

ensureCapacityInternal方法:

保证数组的空间容量足够

//保证数组内部容量
private void ensureCapacityInternal(int minCapacity) {//size+1
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    //
}
​
//计算数组容量
private static int calculateCapacity(Object[] elementData, int minCapacity){
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//判断是否是无参构造,是无参构造,则返回默认值或设定的最小容量(size+1)
        return Math.max(DEFAULT_CAPACITY, minCapacity);//设定长度小于默认值则返回默认值(默认值见静态常量)
    }
    return minCapacity; //有参构造直接返回最小容量
}
​
//确定这个容量能满足
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0)//需要的最小容量比你集合中底层实现数组的长度还长,不能保证容量,需要进行扩容
        grow(minCapacity);//扩容
}
​
//数组扩容
private void grow(int minCapacity) {//10
    int oldCapacity = elementData.length; //创建临时变量,存储数组长度旧值
    int newCapacity = oldCapacity + (oldCapacity >> 1);//1.5倍扩容
    //oldCapacity >> 1  等价于  oldCapacity / 2
    if (newCapacity - minCapacity < 0)//1.5倍扩容之后仍不满足所需容量
        newCapacity = minCapacity; //直接使用所需容量值作为数组容量。
    if (newCapacity - MAX_ARRAY_SIZE > 0)//满足需求
        newCapacity = hugeCapacity(minCapacity);//检查所需容量是否正常,并判断需求容量是否过大,限制最大容量为 MAX_ARRAY_SIZE
    //扩容
    elementData = Arrays.copyOf(elementData, newCapacity);
}
//检查所需容量
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity>MAX_ARRAY_SIZE)?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
} //private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    //public static final int   MAX_VALUE = 0x7fffffff;
    //MAX_ARRAY_SIZE == 2^31 - 9

addAll()方法

//直接尾插
public boolean addAll(Collection<? extends E> c) { 
    Object[] a = c.toArray();// 将集合C转换成数组
    int numNew = a.length; 
    ensureCapacityInternal(size + numNew);  //保证数组内部容量--扩容处理,大小为size + numNew
    System.arraycopy(a, 0, elementData, size, numNew); //数组拷贝,插入拷贝值
    size += numNew;
    return numNew != 0;
}
​
//指定位置插入
public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);  //对添加操作进行索引范围检查,确保在执行添加操作时,所指定的索引处于有效的范围内
        Object[] a = c.toArray(); // 将集合C转换成数组
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  
        int numMoved = size - index;//ArrayList容器数组向右移动的位置长度
        if (numMoved > 0)
            System.arraycopy(elementData, index,
                             elementData, index + numNew,numMoved);//移位插入
        System.arraycopy(a, 0, elementData, index, numNew);//尾插
        size += numNew;
        return numNew != 0;
    }

set()方法

public E set(int index, E element) {
    rangeCheck(index);
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

remove()方法

//删除指定下标位置元素
public E remove(int index) {
    rangeCheck(index);//位置验证
    modCount++;
    E oldValue = elementData(index);//需要删除的元素
    int numMoved = size - index - 1; //左移长度
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved); //数组拷贝并左移
    elementData[--size] = null; //置空最后一个元素
    return oldValue;
}
​
//位置验证
private void rangeCheck(int index) {
    if (index >= size) //检查下标是否超出
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
​
//删除指定值
public boolean remove(Object o) {
    if (o == null) { //因为ArrayList中允许存在null,所以需要判断指定值是否null
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);//移除空值点
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index); //移除等值点
                return true;
            }
    }
    return false;
}
​
//移除指定位置的元素
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; 
}

removeAll()方法

//是继承自AbstractCollection的方法,ArrayList本身并没有提供实现。
public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);//检查传入的对象是否为null
    return batchRemove(c, false);
}
​
//检查传入的对象是否为null,为null抛出NullPointerException异常
public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}
​
//
private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;//获取ArrayList对象的内部数组
    int r = 0, w = 0; //初始化两个变量r和w,用于遍历数组和记录有效元素的索引
    boolean modified = false; //标记是否有元素被移除
    try {
        for (; r < size; r++) //遍历内部数组elementData,判断集合c中是否包含当前元素
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];//将当前元素复制到elementData的新索引位置w,并递增w
    } finally {
        if (r != size) {//判断是否还有未处理的元素
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;//拷贝数组,将剩余未处理的元素复制到新索引位置w开始的位置,并更新w的值
        }
        if (w != size) { //判断是否有未处理的元素
            for (int i = w; i < size; i++)
                elementData[i] = null;//将未处理的元素置为null
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;//有元素被移除
}

get()方法

ArrayList提供了get(int index)用读取ArrayList中的元素。由于ArrayList是动态数组,所以我们完全可以根据下标来获取ArrayList中的元素,而且速度还比较快,故ArrayList常用于随机访问。

public E get(int index) {
    rangeCheck(index);//检查下标是否超出
    return elementData(index); //返回下标位置的值
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Felix_子兮

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

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

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

打赏作者

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

抵扣说明:

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

余额充值