Java Collection集合类- ArrayList 源码分析

在这里插入图片描述

ArrayList 简介

ArrayList的底层是数组,相当于动态数组,它的容量能动态增长.

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

它继承了Abstract,实现了List,RandomAccess(随机访问),Cloneable(克隆),java.io.Serializable(序列化)接口

因为其底层为数组,因而删除元素的时间复杂度为O(n),求长度以及增加元素的,取元素的复杂度为O(1).

ArrayList实现了RandomAccess接口,RandomAccess是一个标志接口,表名实现这个接口的集合是支持快速随机访问的.

ArrayList实现了Cloneable接口,即覆盖实现了clone(),能被克隆.

ArrayList实现了java.io.Serializable接口,说明ArrayList支持序列化,能进行序列化传输.

ArrayList中的操作不是线程安全的.因此在单线程中可以使用ArrayList,避免在多线程中使用.

ArrayList核心源码
1. ArrayList的构造函数
 /**
     * 默认初始容量大小 
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空数组(创建的空实例)
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 用于默认大小实例的共享空数组实例
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     *用于保存ArrayList数据的数组
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * ArrayList所含元素个数
     *
     * @serial
     */
    private int size;

    /**
     * 带初始容量的构造函数(用户自定义的容量) 
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
			//创建initialCapacity大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
        	//创建空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
          //初始容量小于0,抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * 在添加第一个元素时初始化为10
     */
    public ArrayList() {
    //DEFAULTCAPACITY_EMPTY_ELEMENTDATA为空 ,初始为空数组
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 构造一个指定集合的元素的列表,按照它们由集合的迭代器返回的顺序
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 用空数组替换
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
2.分析ArrayList的扩容机制

以无参构造函数创建的ArrayList分析

  1. 首先看add方法
 /**
    *将指定元素添加至列表的末尾
    */
   public boolean add(E e) {
 //  	添加元素之前,先调用ensureCapcityInternal方法
       ensureCapacityInternal(size + 1);  // Increments modCount!!
       elementData[size++] = e;  //为数组赋值
       return true;
   }
  1. 再看ensureCapcityInternal()方法
  private void ensureCapacityInternal(int minCapacity) {
 //此处先调用了  calculateCapacity(elementData, minCapacity)
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
  1. calculateCapacity()方法
//得到最小扩容量
    private static int 
		calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

得到最小的扩容量
此处再添加第一个元素时,size+1 = 1 ,DEEDAULT_CAPACITY 默认为10,返回两者中最大的数.
4. ensureExplicitCapacity()方法
将最小的扩容量返回给 ensureExplicitCapacity()方法之后

/**
*	判断是否需要扩容
**/
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
     //最小扩容量大于其原来的容量
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
  • 当添加第1个元素的时候,因为size+1 < DEFFAULT_CAPCITY,所以第一次扩容值为10
  • 当add第2个元素时,minCapcity为2, if (minCapacity - elementData.length > 0)不成立.所以不进入grow中
  • 直到添加第11个元素的时候,minCapcity (11)- elementDara.length (10)> 0,会进入到grow方法中.
  1. grow方法()
  /**
     *
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;


    private void grow(int minCapacity) {
        // oldCapacity为旧容量        newCapacity为新容量
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);  //向右移位相当于/2,提高效率,新容量为旧容量的1.5倍
	//检查新容量是否小于最少需要容量,如果小于,就把最小需要容量当作数组的新容量
	   if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
	   //如果新容量大于MAX_ARRAY_SIZE,执行hugeCapacity(minCapacity)
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

int newCapacity = oldCapacity + (oldCapacity >> 1),ArrayList每次扩容都为原来的1.5倍
6. hugeCapcity() 方法

 private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
		//对minCapacity和MAX_ARRAY_SIZE进行比较
        //若minCapacity大,将Integer.MAX_VALUE作为新数组的大小
        //若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小
        //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

  1. 源码未使用的ensureCapacity()方法
    /**
    * 提供给用户使用的犯法	
    *add大量数据时,手动指定最小扩容量,提高效率,避免频繁扩容
    */
   public void ensureCapacity(int minCapacity) {
       int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
           // any size if not default element table
           ? 0
           // larger than default for default empty table. It's already
           // supposed to be at default size.
           : DEFAULT_CAPACITY;

       if (minCapacity > minExpand) {
           ensureExplicitCapacity(minCapacity);
       }
   }
容易忽视的知识点:
  • java 中的 length属性是针对于数组来说的,声明了数组,数组本身就有length属性
  • java 中的 length()方法是针对于字符串数的,想知道字符串的长度则调用 length() 方法
  • java 中的 size() 方法是针对于泛型集合来说的,此方法返回的是泛型的元素个数
System.arraycopy()方法与Arrays.copyOf() 回顾
 public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

System.arraycopy()传入的参数依次是 源数组 源数组的起始位置 目标数组 目标数组的起始位置 要复制的元素数量
System.arraycopy() 是浅拷贝, 不生成新的数组

public static int[] copyOf(int[] original, int newLength) {
		//创建新的数组
        int[] copy = new int[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

Array.copyOf()底层还是访问的时 System.arraycopy(),但是其创建了新的数组,因而是深拷贝.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值