ArrayList源码阅读

ArrayList源码阅读

基于jdk1.8 大致从:实现/继承,基础属性,构造方法,添加元素,获取元素,删除元素几个方面

实现/继承

ArrayList实现List接口,List继承Collection接口,Collection继承Iterable接口。

package java.util;
//该接口位于java.util包下

ArrayList:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
//实现了List接口
//RandomAccess:内部任何实现也没有,称为标识接口

List:

public interface List<E> extends Collection<E>
//List接口主要是继承Collection接口

RandomAccess:

public interface RandomAccess {
}
//这就是RandomAccess接口的全部了,内部任何实现也没有

Collection

public interface Collection<E> extends Iterable<E> {
}

Iterable

public interface Iterable<T>{
  
}

基础属性

//ArrayList默认初始化大小
private static final int DEFAULT_CAPACITY = 10;
//空实例的数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//空实例的默认大小数组;与EMPTY_ELEMENTDATA的区别是第一个元素添加时,会进行扩容
 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//ArrayList元素的缓冲区;ArrayList的容量是此数组缓冲区的长度
//当一个空的ArrayList的数组元素为:DEFAULTCAPACITY_EMPTY_ELEMENTDATA时
//添加第一个元素的时候将会扩容为默认容量大小
transient Object[] elementData;
//ArrayList容量大小
private int size;

构造方法

//无参构造
public ArrayList();
//有参构造
public ArrayList(int initialCapacity);
//有参构造,接收一个已有集合
public ArrayList(Collection<? extends E> c);

无参构造:

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
//使用无参构造方式时,ArrayList的初始容量为空

有参构造:

public ArrayList(int initialCapacity) {
 				//实例化大小 > 0的时候
        if (initialCapacity > 0) {
          //按照传入实例化容量大小,实例化一个同样大小的ArrayList的缓冲区
            this.elementData = new Object[initialCapacity];
          //实例化大小 = 0的时候
        } else if (initialCapacity == 0) {
          	//ArrayList的缓冲区为空
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

有参构造,接收一个已有集合:

public ArrayList(Collection<? extends E> c) {
  			//ArrayList的缓存数组为传入的集合的大小
        elementData = c.toArray();
  			//判断传入的集合是否为空
  			//不为空
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
          	//如果传入的集合不是Object类型
            if (elementData.getClass() != Object[].class)
              	//复制一个Object类型数组
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
          	//否则实例化为一个空的数组
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

添加元素

ArrayList添加元素的方法为add()添加单个元素addAll()添加全部元素

add():

  • 按序添加:将指定元素添加至list末尾
  • 添加元素到指定下标

按序添加:将指定元素添加至list末尾

public boolean add(E e) {
  			//要添加元素了,要看看当前集合是否有容量可以用
  			//先给当前集合容量+1
  			//就是判断当前集合是否是第一次添加元素
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //要添加的元素添加到集合缓冲数组中,同时集合大小自增1
  			elementData[size++] = e;
        return true;
    }

ensureCapacityInternal:

 private void ensureCapacityInternal(int minCapacity) {
   			//calculateCapacity集合缓存数组与最小容量比较,如果是第一次添加元素,将集合扩容为默认大小
   			//ensureExplicitCapacity
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

calculateCapacity:

private static int calculateCapacity(Object[] elementData, int minCapacity) {
  			//如果集合缓冲数组为空
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
          	//取集合默认容量大小与传入的最小容量的最大值
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
  			//如果不为空,返回最小容量
        return minCapacity;
    }

ensureExplicitCapacity

private void ensureExplicitCapacity(int minCapacity) {
  			//modCount:AbstractList抽象类中的属性,操作次数
  			//主要是为了实现fail-fast机制(快速失败机制),在遍历集合的时候对集合做结构性变化检查,即看看有没有对集合元素做删除操作
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
          	//扩容方法,防止造成内存溢出
            grow(minCapacity);
    }

grow

//确保集合容量可以容纳最小容量参数指定的元素数
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
  	//扩容大小为1.5倍,即原数组大小+原数组大小的1/2
    int newCapacity = oldCapacity + (oldCapacity >> 1);
  	//如果新数组大小 < 最小容量大小
    if (newCapacity - minCapacity < 0)
      	//新数组大小=最小容量大小
        newCapacity = 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);
}

hugeCapacity

//集合大小溢出问题
private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
  			//如果最小容量大于集合允许的最大容量,则最小容量为Integer的最大值 2^31 约等于 21亿
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
//list允许最大容量:Integer.MAX_VALUE - 8
//数组需要8bytes的存储大小表示数组的长度等元数据,
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
Integer.MAX_VALUE = 2^31 = 2147483648

数组对象的元数据:

  • Class:只想描述对象类型的类信息的指针。
  • Flag:描述对象状态的标志的集合,包括对象的散列码以及对象的形状(即对象是否为数组)。
  • Lock:对象的同步信息(即对象当前是否同步)。
  • Size:数组的大小。

添加元素总结:

  • 首先会判断是否为第一次添加元素,如果为第一次添加元素,会将数组大小扩容为10
  • 数组操作次数自增1,判断是否需要扩容;扩容类型分为默认1.5倍扩容,最小容量扩容,最大容量扩容。最大容量扩容又分为:数组最大容量和Integer最大容量
  • 添加元素到list缓冲数组

添加元素到指定下标

public void add(int index, E element) {
  			//数组边界检查,检查要插入的下标是否超出集合边界
        rangeCheckForAdd(index);
				//数组结构性检查和扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
  			//将数组从下标index开始,都往后移动一位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
  			//将元素插入到index位置
        elementData[index] = element;
  			//list大小自增1
        size++;
    }

rangeCheckForAdd

private void rangeCheckForAdd(int index) {
  			//数组边界检查
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

指定下标添加元素总结:

  • 数组边界检查,检查要插入的下标是否超出集合边界
  • 数据结构性检查和数组扩容
  • 数组移动
  • 元素插入和size自增1

获取元素

ArrayList获取元素的方法为get()

get()

public E get(int index) {
  			//边界检查
        rangeCheck(index);
				//从list缓存数组中获取值
        return elementData(index);
    }

rangeCheck

//数组下标边界检查
private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

elementData

//从list缓存数组中获取值
E elementData(int index) {
        return (E) elementData[index];
    }

获取元素总结:

  • 数组下标边界检查
  • 从list缓存数组中获取值

删除元素

ArrayList删除元素的方法是remove()

分为:

  • 根据索引下标进行删除
  • 根据元素对象进行删除

remove(int index):

根据索引下标删除

public E remove(int index) {
  			//下标边界检查
        rangeCheck(index);
				//操作次数自增
        modCount++;
        E oldValue = elementData(index);
				//数组删除后要移动元素个数
        int numMoved = size - index - 1;
        if (numMoved > 0)
          	//数组从index+1及以后元素移动到index,直接覆盖
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
  			//最后一个元素置空,且数组长度减一
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

删除元素总结:

  • 下标边界检查
  • 操作次数自增
  • 数组删除后要移动元素个数,数组从index+1及以后元素移动到index及以后,直接覆盖index下标的值
  • 最后一个元素置空,且数组长度减一

remove(Object o)

根据元素对象删除

public boolean remove(Object o) {
  			//如果要删除的元素为空
        if (o == null) {
          	//遍历集合
            for (int index = 0; index < size; index++)
              	//如果查询出来为null == 
                if (elementData[index] == null) {
                  	//删除
                    fastRemove(index);
                    return true;
                }
          //删除元素不为空
        } else {
          	//遍历集合
            for (int index = 0; index < size; index++)
              	//equals方法
                if (o.equals(elementData[index])) {
                  	//删除元素
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

fastRemove():快速删除方法

 private void fastRemove(int index) {
   			//操作次数自增
        modCount++;
   			//要移动元素
        int numMoved = size - index - 1;
   			//如果不是最后一个值
        if (numMoved > 0)
          	//移动,覆盖
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
   			//数组最后一个元素置空(gc 回收),长度减一
        elementData[--size] = null; // clear to let GC do its work
    }

iterator的ArrayList内部类Itr

Itr是实现iterator接口的ArrayList内部类

private class Itr implements Iterator<E> {
  			//两个游标
  			//下一个元素的下标
        int cursor;       // index of next element to return
  			//最后返回的元素的下标
        int lastRet = -1; // index of last element returned; -1 if no such
  			//期望操作次数 和 操作次数 :用于检测集合的结构性变化
        int expectedModCount = modCount;
				//无参构造
        Itr() {}
}

hashNext()

//判断是否遍历结束
public boolean hasNext() {
  					//下一个元素的下标与集合大小
            return cursor != size;
        }

next()

//获取集合的下一个元素
public E next() {
  					//检查集合结构性变化,就是检查期望操作次数 和 操作次数是否一致
            checkForComodification();
            int i = cursor;
  					//检查要获取元素的下标,是否超出集合大小
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
  					//检查要获取元素的下标,是否超出集合缓冲数组大小
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
  					//更新指向下一个元素的下标游标
            cursor = i + 1;
  					//返回当前元素,更新最后返回元素的下标游标
            return (E) elementData[lastRet = i];
        }

checkForComodification

//检查集合结构性变化
final void checkForComodification() {
  					//就是检查期望操作次数 和 操作次数是否一致
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

remove

public void remove() {
  					//检查要删除元素下标边界
            if (lastRet < 0)
                throw new IllegalStateException();
  					//集合结构性检查
            checkForComodification();

            try {
              	//调用ArrayList根据下标索引删除的方法
                ArrayList.this.remove(lastRet);
              	//更新两个游标的值,和期望操作次数和操作次数的值
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

forEachRemaining

对集合中剩余的元素进行遍历输出,简单来说,如果在集合中有10个元素,先遍历输出了5个,再使用forEachRemaining的时候只会输出5个元素。

 				@Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
          	//将下一个元素的下标赋值给了要输出元素的下标,从这里就可以看出来 forEachRemaining是做什么的,就是输出集合中剩下的元素
            int i = cursor;
          	//检查输出下标边界
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
          	//输出下标边界检查,期望操作次数与操作次数检查
            while (i != size && modCount == expectedModCount) {
              	//函数式接口,输出
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
          	//两个游标值更新
            cursor = i;
            lastRet = i - 1;
          	//集合结构性检查
            checkForComodification();
        }
				
				//集合结构性检查
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值