ArrayList 源码分析01之构造函数、add()、get()、remove()

ArrayList 是实现了 List 接口的大小可变数组,实现了所有可选列表操作,运行Null在内的所有元素。

ArrayList 详解

  • 从类定义可知,ArrayList 是一个列表,并且支持快速随机访问(RandomAccess)、可复制(Cloneable)、可序列化(java.io.Serializable)
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

1. ArrayList 的构造函数

  • ArrayList 有 3 个构造函数
    • ArrayList():初始化容量为 0 的列表。
    • ArrayList(int initialCapacity):初始化指定容量的列表。
    • ArrayList(Collection<? extends E> c):初始化时,将一个列表传入,将列表的每个元素对象赋值给初始化的列表。
public ArrayList() {
	/* 默认的空数组 Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA= {} */
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
	/* 创建对应长度的数组 */
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    /* 为了扩容,与空构造区别,但同样是空数组 Object[] EMPTY_ELEMENTDATA = {}*/
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}
public ArrayList(Collection<? extends E> c) {
	/* 将传入的 c 转化数组 */
    elementData = c.toArray();
    /* 传入的 c 含有元素 */
    if ((size = elementData.length) != 0) {
        /* 有必要的话,将传入的 c 中的元素对象转为 Object */
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // size 为0,代表 c 为空列表
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

2. ArrayList 的静态变量

/* 版本号 */
private static final long serialVersionUID = 8683452581122892189L;
/* 默认容量大小为 10 */
private static final int DEFAULT_CAPACITY = 10;
/* 构造函数 initialCapacity 为0 是赋值给 elementData  */
private static final Object[] EMPTY_ELEMENTDATA = {};
/* 构造函数 参数为空 赋值给 elementData */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/* 数据缓冲区,ArrayList 的容量就是缓冲区的长度 */
transient Object[] elementData;
/* ArrayList最大容量,为什么是 Integer.MAX_VALUE - 8,官方的解释是避免 OutOfMemoryError,但在扩容时,实际上最大容量还是 Integer.MAX_VALUE。
网上搜索一大堆 -8 是因为对象头数组有一个占 8 字节的数组数据,这没错,但是有关系吗?不想想 int 才 4 字节吗?怎么减 8 字节的长度?
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

3. 小总结

  • 构造函数与静态变量构成一个列表应有的基本数据,初始化长度,列表的最大长度。
  • 其他的添加、删除操作,应该属于每个实例的功能了,即非静态方法(普通方法)。
  • 可能有人会问,扩容参数呢?好问题,都知道扩容为1.5倍原有的长度,即刚好可以利用右移操作,int newCapacity = oldCapacity + (oldCapacity >> 1) 源码就是这么扩容的。对于这一块知识不太懂的,可以观看我的另一篇博客,java 位运算(位与&、或|、异或^、非~、移位>>、<<)的简单理解

4. 各种操作函数

4.1 添加 add()

4.1.1 boolean add(E e)
public boolean add(E e) {
	/* 将 size+ 1 传入,检查添加操作,需不需要扩容 */
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    /* 给 elementData 当前长度的下一位赋值 */
    elementData[size++] = e;
    return true;
}
/* ensureCapacityInternal分为两步
 * 1. calculateCapacity(elementData, minCapacity)
 * 2. ensureExplicitCapacity(int minCapacity)
 */
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/* 1.calculateCapacity: 判断当前的 elementData 是否空构造,
 * 为 true 返回 DEFAULT_CAPACITY 与 minCapacity 的最大值
 * 为 false 直接返回 minCapacity
 */
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
/* 2. ensureExplicitCapacity:
 * 2.1 集合修改次数++
 * 2.2 判断是否需要扩容 
 */
private void ensureExplicitCapacity(int minCapacity) {
	/* ArrayList 集合被修改次数,主要供迭代器 iterator 使用 */
    modCount++;
    // 如果列表需要的最小容量 > 当前elementData 的长度,那么进行扩展
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
  • 接下来看扩容函数
/* 主要有以下 3 步
 * 1. 获取列表旧容量,根据旧容量计算扩容后的新容量(1.5倍)
 * 2. 比较新旧容量的值
 * 	2.1 newCapacity - minCapacity < 0 即扩容后的容量大小比要求的还小,直接使用需要的最小容量,这里出现的原因是添加的集合元素很多
 *  2.2 newCapacity - MAX_ARRAY_SIZE > 0 即扩容后的容量比静态变量 MAX_ARRAY_SIZE 规定的最大容量还大,执行 hugeCapacity(minCapacity);
 * 3. 将旧elementData 中的值 copy 到新elementData 中
 */
private void grow(int minCapacity) {
	/* 1. */
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    /* 2. */
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    /* 3. 真正的扩容操作 */
    elementData = Arrays.copyOf(elementData, newCapacity);
}
/* 巨大容量函数,ArrayList 的最大容量还是 Integer.MAX_VALUE,所以 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 可能是想预留着用来干啥,没办法的话,还是的拿出来用吧 */
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}
  • 至此,add(E e) 操作完毕,步骤就是,判断需不需要扩容,会不会超过最大容量,不需要的话,直接进行 elementData[size++] = e; 赋值。
4.1.2 void add(int index, E element)
  • 与 add(E e) 对比,多了一个 index,指定位置赋值,但必须在已有的size范围内,因为随意指定会造成极大的空间浪费
public void add(int index, E element) {
	/* 对 index 位置进行判断,(index > size || index < 0),只能在size范围内进行添加,并且不能小于 0 */
    rangeCheckForAdd(index);
    /* 跟add()一样,判断需不需要扩容 */
    ensureCapacityInternal(size + 1);
    /* 因为中间被插入了一个值,所以,需要将index后面的值整体移后一位 */
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    /* 位置赋值操作 */
    elementData[index] = element;
    /* 有添加,元素个数 + 1 */
    size++;
}
4.1.3 boolean addAll(Collection<? extends E> c)
  • 将一个集合 c (不局限于ArrayList)直接添加进来
public boolean addAll(Collection<? extends E> c) {
	/* 1. 将集合转换成数组,并得到数组的长度 */
    Object[] a = c.toArray();
    int numNew = a.length;
    /* 2. 与 add() 相比,这里是直接添加多个,可能会出现扩容1.5倍比minCapacity小的情况,其余的情况相同 */
    ensureCapacityInternal(size + numNew);
    /* 将集合转换的数组 a 添加进 elementData */
    System.arraycopy(a, 0, elementData, size, numNew);
    /* 这次 add的长度是 集合转成的数组长度 */
    size += numNew;
    /* numNew 为0 表示没有任何元素添加进 ArrayList,返回 false */
    return numNew != 0;
}
4.1.4 boolean addAll(int index, Collection<? extends E> c)
  • 在指定位置添加集合
public boolean addAll(int index, Collection<? extends E> c) {
	/* 检查 index 范围是都在 size 范围内 */
    rangeCheckForAdd(index);
	/* 获取集合长度,判断是否需要扩容 */
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
	/* 计算需要移动的元素个数,先移动,后添加 */
    int numMoved = size - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);

    System.arraycopy(a, 0, elementData, index, numNew);
    /* size += 添加元素的个数 */
    size += numNew;
    /* 个数为0 返回 false */
    return numNew != 0;
}
4.1.5 小总结
  • add(E e) 是在 list 的最末尾添加一个元素,add(int index, E e) 是在指定位置(size范围内)添加一个元素
    • add(E e) 结果等同于 add(arrayList.size(), E e),只不过不用整体进行 index 后的整体移位操作,虽然也没有
  • addAll(Collection<? extends E> c) 将整个集合添加,若 c.size == 0 则返回false,addAll(int index, Collection<? extends E> c) 在指定位置(size范围内)添加集合
    • addAll(Collection<? extends E> c) 结果等同于 addAll(int index, Collection<? extends E> c)
import java.util.ArrayList;
import java.util.LinkedList;

public class AddTest {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        System.out.println("new ArrayList()后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size());
        arrayList.add(10);
        System.out.println("add(1)后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size());
        arrayList.add(20);
        System.out.println("add(2)后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size());
        arrayList.add(1, 15);
        System.out.println("add(1, 15)后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size());

        System.out.println("============== 华丽分割线 ==============");
        /* 一行添加元素 */
        ArrayList<Integer> arrayList01 = new ArrayList<Integer>() {{
            add(30);
            add(40);
        }};

        LinkedList<Integer> linkedList = new LinkedList<Integer>() {{
            add(50);
            add(100);
        }};
        boolean flag = arrayList.addAll(arrayList01);
        System.out.println("addAll(arrayList01)后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size() + "\t 是否添加成功" + flag);

        boolean flag01 = arrayList.addAll(new ArrayList<>());
        System.out.println("addAll(new ArrayList<>())后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size() + "\t 是否添加成功" + flag01);

        boolean linkecFlag = arrayList.addAll(linkedList);
        System.out.println("addAll(linkedList)后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size() + "\t 是否添加成功" + linkecFlag);
        boolean linkecFlag01 = arrayList.addAll(new LinkedList<>());
        System.out.println("addAll(new LinkedList<>())后, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size() + "\t 是否添加成功" + linkecFlag01);

        System.out.println("=============== 注意:要么都使用泛型,要么都不用 ==============");
        ArrayList nonGeneric = new ArrayList() {
            {
                add("a50");
                add(new Object());
            }
        };
        arrayList.addAll(nonGeneric);
        System.out.println("addAll() 添加没有使用泛型的 ArrayList, 当前 arraylist --> " + arrayList + "\t 当前 size " + arrayList.size());
    }
}

在这里插入图片描述

4.2 获取 get(int index)

  • 根据下标获取列表的值,但不移除
public E get(int index) {
	/* 检查 index 范围,>= 报错,负数数组自身就会报错 */
    rangeCheck(index);
    return elementData(index);
}
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/* 返回值之前,因为泛型擦除的原因,需要进行一次强转 */
E elementData(int index) {
    return (E) elementData[index];
}
  • 代码演示
package cn.cerish.container.collection.arrayList;

import java.util.ArrayList;

public class GetTest {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        arrayList.add(30);
        System.out.println("当前 ArrayList :" + arrayList);

        System.out.println("获取列表第一个值(下标从0开始): " + arrayList.get(0));
        System.out.println("获取列表第二个值(下标从0开始): " + arrayList.get(1));
        System.out.println("获取列表第三个值(下标从0开始): " + arrayList.get(2));
        System.out.println("获取列表第四个值(下标从0开始),没有,那就报错: " + arrayList.get(3));
    }
}

在这里插入图片描述

4.3 删除 remove

4.3.1 remove(int index)

  • 移除指定下标的元素
public E remove(int index) {
	/* 检查 index 范围 */
    rangeCheck(index);
	/* 修改次数++ */	
    modCount++;
    /* 获取指定位置的元素 */
    E oldValue = elementData(index);
	/* 获得需要移动的个数,index后面的元素往前移一位,最后一位置为null,方便GC */
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
	/* 返回旧值 */
    return oldValue;
}

4.3.2 remove(Object o)

  • 删除指定的元素
public boolean remove(Object o) {
	/* 区别 o 是否为 null,避免空指针异常 */
    if (o == 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;
            }
    }
    /* 没有找到,返回false */
    return false;
}
/* 移除函数 fastRemove(index); */
private void fastRemove(int index) {
	/* 修改次数++,将index后的元素左移一位,最后一位置为null */
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

4.3.3 remove(Collection<?> c)

  • 删除 集合 c 中共同的元素,有删除成功的返回 true,否则为 false
 public boolean removeAll(Collection<?> c) {
 	/* 非空检查 */
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}
/* batchRemove 批量移除函数 */
private boolean batchRemove(Collection<?> c, boolean complement) {
	
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try {
    	/* for循环取出 elementData[r],判断 c 集合是否含有该元素,为true不做操作,为false 记录进 elementData[w++],即从0开始记录,后面的会被删除 */
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
        /* r != size 即抛出过错误,需要将未执行 contains() 的元素加入已比对的 w 身后 */
        if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        /* w != size 代表数据有被移除,将 w 后的个数(即移除个数)置为null,方便GC */
        if (w != size) {
            // clear to let GC do its work
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    /* 可以看到,有移除的元素才返回 true,否则为 false */
    return modified;
}
4.3.4 boolean retainAll(Collection<?> c)
  • 移除不在集合 c 内的元素
public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    /* 这里第二个参数为 true,刚好跟removeAll()的 false 相反 */
    return batchRemove(c, true);
}

4.3.5 removeIf(Predicate<? super E> filter)

  • 移除符合条件的元素
public boolean removeIf(Predicate<? super E> filter) {
	/* 检查过滤器不能为null */
    Objects.requireNonNull(filter);
    // figure out which elements are to be removed
    // any exception thrown from the filter predicate at this stage
    // will leave the collection unmodified
    int removeCount = 0;
    final BitSet removeSet = new BitSet(size);
    final int expectedModCount = modCount;
    final int size = this.size;
    /* for循环逐个元素验证符合 filter 条件 */
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        @SuppressWarnings("unchecked")
        final E element = (E) elementData[i];
        /* 满足条件的记录下标 */
        if (filter.test(element)) {
            removeSet.set(i);
            removeCount++;
        }
    }
    /* 不相等则表示 modCount被修改过,抛出错误 */
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }

    // shift surviving elements left over the spaces left by removed elements
    /* removeCount > 0 即有移除的元素,才有必要进行移位赋值 */
    final boolean anyToRemove = removeCount > 0;
    if (anyToRemove) {
    	/* 这段代码的例子解释,源列表[0,1,2,3,4],欲删除的位置为1,3,即removeSet下标1,3为true
    	 * 1. 当 i =0, removeSet.nextClearBit(i); 返回0,当 i = 1,返回2,elementData[1] = elementData[2]; 即列表变成[0,2,2,3,4]
    	 * 2. 照此推理, 列表最后变成[0,2,4,3,4] 可自行debugger验证
    	 */
        final int newSize = size - removeCount;
        for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
        	/* nextClearBit() 返回下一个为 false 的下标 */
            i = removeSet.nextClearBit(i);
            elementData[j] = elementData[i];
        }
        /* 将后面的元素置空(移除&移位后空出来的位置),方便GC */
        for (int k=newSize; k < size; k++) {
            elementData[k] = null;  // Let gc do its work
        }
        this.size = newSize;
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

    return anyToRemove;
}
4.3.6 小总结
  • E remove(int index):移除指定位置的元素,返回被移除的元素
  • boolean remove(Object o):找到删除返回true,否则返回false
  • boolean removeAll(Collection<?> c):移除与 集合 c 有相同的元素,如果有,返回true,否则返回 false
  • boolean retainAll(Collection<?> c):移除与 集合 c 没有交集的元素,如果有,返回true,否则返回 false
  • boolean removeIf(Predicate<? super E> filter):移除符合条件的元素,如果有,返回true,否则返回 false
import java.util.ArrayList;
import java.util.BitSet;

public class RemoveTest {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        arrayList.add(30);
        arrayList.add(40);
        arrayList.add(50);
        arrayList.add(60);
        System.out.println("now arraylist: " + arrayList);
        Integer remove0 = arrayList.remove(0);
        System.out.println("移除的 0 位元素:" + remove0 + "\t now arraylist: " + arrayList);
        System.out.println("==========================================");
        /* 这里如果要删除30这个元素,需要传入Integer对象,否则会被当做下标处理 */
        Integer integer30 = new Integer(30);
        boolean remove30 = arrayList.remove(integer30);
        System.out.println("移除指定的元素:30 --> " + remove30 + "\t now arraylist: " + arrayList);
        Integer integer300 = new Integer(300);
        boolean remove300 = arrayList.remove(integer300);
        System.out.println("移除指定的元素: 300 --> " + remove300 + "\t now arraylist: " + arrayList);

        System.out.println("==========================================");
        ArrayList<Integer> arrayList01 = new ArrayList<>();
        arrayList01.add(50);
        arrayList01.add(80);
        boolean flag = arrayList.removeAll(arrayList01);
        System.out.println("移除集合中相同的元素: " + flag + "\t now arraylist: " + arrayList);
        /* 没有相同元素的返回 false */
        boolean flag01 = arrayList.removeAll(new ArrayList<Integer>(){{add(100);add(300);}});
        System.out.println("移除集合中相同的元素: " + flag01 + "\t now arraylist: " + arrayList);

        System.out.println("==========================================");
        System.out.println("第一次移除大于50的元素: " + (arrayList.removeIf(it -> it > 50) + "\t now arraylist: " + arrayList));
        System.out.println("第二次移除大于50的元素: " + (arrayList.removeIf(it -> it > 50) + "\t now arraylist: " + arrayList));

    }
}

在这里插入图片描述

5 总结

  • add() 主要分为两种,指定位置(没有指定默认最末尾 size)添加单个元素与集合(多个元素)
  • get() 用于获取指定位置的元素,内部使用了泛型强转,所以在使用的时候不用进行强转。
  • remove 分为五种,移除指定位置,移除指定元素,移除指定集合具有 相同 or 不相同 的元素,移除符合指定条件的元素
  • 避免篇幅过长,下一篇见。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值