集合框架源码之ArrayList

集合框架源码

ArrayList

介绍

ArrayList是Collection接口中List接口的实现类,是一种有序可重复的单列集合,集合底层采用数组进行数据存储。

ArrayList是Java集合框架中的一个重要类,它实现了List接口,用于存储元素的有序集合。以下是ArrayList的优缺点分析:

优点

  1. 动态长度:ArrayList的长度是动态的,可以根据需要自动增长或缩减。当元素数量超出当前容量时,ArrayList会自动进行扩容,以适应更多的元素。这一特性使得ArrayList在存储元素时更加灵活,无需像静态数组那样在初始化时确定大小。
  2. 基于数组实现:ArrayList内部是基于数组实现的,这意味着可以通过索引快速访问元素,时间复杂度为O(1)。这种基于数组的实现方式使得ArrayList在随机访问方面具有较高的效率。
  3. 允许重复元素和null:ArrayList可以包含重复的元素,同时也可以存储null元素。这一特性使得ArrayList在存储具有重复性和空值的数据时更加灵活。
  4. 实现了List接口:ArrayList实现了List接口,因此支持List接口的所有操作,如按索引访问、添加、删除和遍历等。这使得ArrayList具有丰富的操作方法,能够满足不同的需求。
  5. 遍历效率高:由于ArrayList是基于数组的,且支持随机访问,因此在进行遍历操作时具有较高的效率。通过索引可以快速地访问到每个元素,从而提高了遍历的速度。

缺点

  1. 动态扩展的开销:虽然ArrayList具有动态扩展的特性,但这种扩展操作可能会带来一定的性能开销。当数组达到容量限制时,ArrayList需要重新分配更大的内存块,并将现有元素复制到新的内存位置。这种操作在大型数据集上可能会导致性能下降。
  2. 内存占用:ArrayList会为每个元素分配内存,这可能会导致内存占用较大,特别是在存储大量数据时。如果内存是一个有限资源,这可能成为一个问题。
  3. 不适合频繁插入和删除:在ArrayList的开始位置或中间位置插入或删除元素时,需要移动大量的元素来保持顺序,这会导致较高的时间复杂度(O(n))。相比之下,LinkedList在插入和删除操作方面具有较高的效率。
  4. 缺乏内建排序支持:ArrayList不具备内建的排序功能,因此如果需要对列表中的元素进行排序,需要手动实现或使用额外的排序算法。
  5. 不支持基本数据类型:ArrayList只能存储对象,而不支持原始数据类型(例如int、char、double等)。因此,对于大量的原始数据类型,需要将它们包装为对象,这可能会导致装箱和拆箱开销。
  6. 线程安全性:ArrayList不是线程安全的。如果多个线程同时访问和修改ArrayList,可能会导致竞争条件和数据不一致性。虽然可以通过外部同步来解决这个问题,但会增加编程的复杂性和性能开销。

综上所述,ArrayList具有动态长度、基于数组实现、允许重复元素和null、实现了List接口以及遍历效率高等优点;但同时也存在动态扩展开销、内存占用大、不适合频繁插入和删除、缺乏内建排序支持、不支持基本数据类型以及线程安全性等缺点。在选择使用ArrayList时,需要根据具体的应用场景和需求进行权衡。

源码解析

成员变量
// 序列化ID,因为ArrayList实现了Serializable   
private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     */
    // 默认初始化数组的长度 10
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    // 长度为0的数组,为空实例准备,EMPTY_ELEMENTDATA是一个空的数组实例,通常用于表示那些没有显式指定初始容量的ArrayList实例。当通过无参构造器创建ArrayList时,它的内部数组就会指向EMPTY_ELEMENTDATA。这意味着在添加第一个元素之前,ArrayList不占用除了对象头以外的额外空间。
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     * 这个数组实例是共享的,用于所有通过无参构造器创建的ArrayList实例。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added. 存储数据的数组
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     * 数组的当度
     */
    private int size;

DEFAULTCAPACITY_EMPTY_ELEMENTDATAEMPTY_ELEMENTDATA的区别

  • 用途不同EMPTY_ELEMENTDATA也用于表示空的ArrayList,但它通常用于其他场景,比如当通过有参构造器传入初始容量为0时,或者在某些内部方法中用于表示一个空的集合状态。而DEFAULTCAPACITY_EMPTY_ELEMENTDATA则专门用于无参构造器创建的ArrayList实例。
  • 扩容行为:由于EMPTY_ELEMENTDATADEFAULTCAPACITY_EMPTY_ELEMENTDATA在初始化时都是空的数组,它们在添加第一个元素时都会触发扩容。但关键在于,DEFAULTCAPACITY_EMPTY_ELEMENTDATA使得ArrayList能够知道这是通过无参构造器创建的实例,因此在扩容时通常会选择一个默认的初始容量(如10),而不是简单地根据当前容量(0)来计算新容量。
构造方法
/**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     * 初始化数组的长度
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            // 如果输入值>0 数组的长度为initalCapacity
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            // 如果输入值为0  将EMPTY_ELEMENTDATA的值给到elementData
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            // 抛出参数不合法的异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     * 默认构造将DEFAULTCAPACITY_EMPTY_ELEMENTDATA的地址给到elementData
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
    * 通过集合来初始化数组
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection<? extends E> c) {
        // 将集合转为Object[]
        elementData = c.toArray();
        // 如果elementData的长度不为0
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            // 判断是否为Object数组
            if (elementData.getClass() != Object[].class)
                // 如果不是Object数组,copy一份Object数组赋予elementData
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
常用方法-add
/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        // 检查容量是否满足,并扩容,将modCount++
        ensureCapacityInternal(size + 1);  // Increments modCount!!
       	// size+1的位置放入新增元素
        elementData[size++] = e;
        // 返回true
        return true;
    }
 private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
 private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            // 扩容
            grow(minCapacity);
    }
/**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // newCapacity为oldCapacity的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            // 如果1.5倍扩容小于需要的长度,直接为需要的长度
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            // 如果数组长度大于Integer.MAX_VALUE - 8,返回Integer.MAX_VALUE
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        // 数组复制
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        // 检查索引是否在范围之内
        rangeCheckForAdd(index);
		// 检查容量是否满足,并扩容,将modCount++
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
常用方法-remove
/**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    public boolean remove(Object o) {
        if (o == 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++)
                // 通过equals判断2个对象是否相同
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(int index) {
        modCount++;
        // 计算删除后需要移动的元素的个数
        int numMoved = size - index - 1;
        if (numMoved > 0)
            // 参数1 源数组
            // 参数2 源数组的起始位置
            // 参数3 目标数组 
            // 参数4 目标数组的起始位置
            // 参数5 复制的源数组中的个数
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 将源数组最后一位设置null
        elementData[--size] = null; // clear to let GC do its work
    }
常用方法-addAll
  public boolean addAll(Collection<? extends E> c) {
        // 参数转为数组
        Object[] a = c.toArray();
        // 获取数组的长度
        int numNew = a.length;
        // 是否需要扩容
        // 这里如果需要扩容,扩容到size+numNew的大小
        ensureCapacityInternal(size + numNew);  // Increments modCount
        // 从a数组的0标位开始复制,复制到elementData中,从size开始,复制的个数为a的长度
        System.arraycopy(a, 0, elementData, size, numNew);
        // size增减numNew
        size += numNew;
        return numNew != 0;
    }
常用方法-removeRange
protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        // 将源数组中的toIndex移动到elemetnData中
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        // 将fromIndex到toIndex之间的设置为null 用于
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }
常用方法-batchRemove
private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                // 需要删除的数据在elementData中被后面的数据替换
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            // r!=size只有在编译的过程中抛出异常才会出现
            // r=1  size=2
            // w=1
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            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;
            }
        }
        return modified;
    }

eg:

List list1 = 1,2,3,4,5;
List list2 = 3;
this.elementData=1,2,4,5,5
finally前
w=4
删除this.elementData[4]
最终this.elementData=1,2,4,5
moudcount 作用
package com.sofwin.configer;

import java.util.ArrayList;
import java.util.Iterator;  
  
public class ModCountExample {  
    public static void main(String[] args) {  
        ArrayList<String> list = new ArrayList<>();  
        list.add("A");  
        list.add("B");  
        list.add("C");  

        new Thread(()->{
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
                String element = iterator.next();
                if (element.equals("B")) {
                    // 在迭代过程中尝试修改集合
                    list.remove(element);
                }
            }
        }).start();


        // 输出修改后的集合
        for (String element : list) {  
            System.out.println(element);  
        }  
    }  
}
1.9.6.jar;D:\mavenrepository\org\javassist\javassist\3.25.0-GA\javassist-3.25.0-GA.jar" com.sofwin.configer.ModCountExample
A
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.sofwin.configer.ModCountExample.main(ModCountExample.java:26)

Process finished with exit code 1

由于在迭代过程中修改了集合,将会抛出ConcurrentModificationException异常
这是因为modCount已经改变,而expectedModCount仍然保持迭代开始时的值

建议写法

  Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }
扩容机制

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_AndyLau

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

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

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

打赏作者

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

抵扣说明:

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

余额充值