ArrayList和LinkedList源码解读

ArrayList & LinkedList & Vector源码分析

1. ArrayList源码分析

1.1ArrayList继承关系和层次结构

  • ArrayList:说明支持了泛型
  • entends AbstractList:继承了AbstractList
  • implements List, RandomAccess, Cloneable, java.io.Serializable
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
{
    private static final long serialVersionUID = 8683452581122892189L;

    //默认的初始化容量
    private static final int DEFAULT_CAPACITY = 10;

    // 指定该ArrayList容量为0时,返回该空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //当调用无参构造方法,返回的是该数组。刚创建一个ArrayList时,其内数据为0
    //它与EMPTY_ELEMENTDATA的区别就是:该数组是默认返回的,而EMPTY_ELEMENTDATA是在用户指定容量为0时返回。
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //保存添加到ArrayList中的元素
 	//ArrayList的容量就是该数组的长度
    //该值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,当第一次添加元素进入ArrayList中时,数组将扩容值DEFAULT_CAPACITY
    //被标记为transient
    transient Object[] elementData; // non-private to simplify nested class access

    //ArrayList的实际大小(数组包含的元素个数/实际数据的数量)默认为0;
    private int size;

1.2构造方法

  • ArrayList提供3中构造方法:
  • ArrayList(int initialCapacity):构造一个指定容量为capacity的空ArrayList,这是一个带初始容量大小的有参构造函数
    • 初始容量大于0,实例化数组,将自定义的容量大小当成初始化elementData的大小
    • 初始容量等于0,将空数组赋值给elementData
    • 初始容量小于0,将抛出异常
//构造具有指定初始容量的空list
public ArrayList(int initialCapacity) {//传入容量大小的参数
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];//如果比0大就创建传入参数大小的容量数组
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA; //如果相等赋值为空数组
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);//穿入负数抛出异常
        }
    }
  • ArrayList() :构造一个初始容量为10的空列表。如果创建ArrayList对象时不传入参数,则使用此无参构造方法创建ArrayList对象。从前面可以知道DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个空的Object[],将elementData初始化,elementData也是Object[]类型。空的Object[]会给默认的容量,但是这里我们并没有看到数组容量变成10,那么是什么时候回被初始化为10的数组呢?答案是有元素被加入时(使用add())。当第一次使用add方法时候,elementData将会变成默认长度:10.
//构造一个初始容量为10的空列表
public ArrayList() {
       this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//
	}
  • ArrayList(Collection<? extends E> c) :(暂时没有搞懂)
public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

1.3主要方法

  • add(e):
//将指定的元素追加到此列表的末尾 
public boolean add(E e) {
    	//确定list容量,如果不够容量加1.注意:只加1,保证资源不被浪费
        ensureCapacityInternal(size + 1);  // Increments modCount!!
    	//将元素e放到size的索引位置上,并size++
        elementData[size++] = e;
        return true;
    }
//数组容量检查,不够时则进行扩容,只供内部类使用
private void ensureCapacityInternal(int minCapacity) {  //传入的是size+1的值
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);//三维运算:true时取DEFAULT_CAPACITY;false取minCapacity
            //若elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA,则取minCapacity为DEFAULT_CAPACITY和参数minCapacity之间最大的值。
            //DEFAULT_CAPACITY在此之前初始化容量为10
        }
        return minCapacity;
    }
//数组容量检查,不够时则进行扩容,只供内部类使用
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
    	//最小容量>数组缓冲区当前长度
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);//扩容
    }
  • add 方法是在list的末尾添加一个元素。size是数组中数据的个数,因为要添加一个元素,所以size+1,先判断size+1的这个个数数组能否放得下。calculateCapacity方法用来计算容量。判断初始化的elementData是不是空的数组,如果是空数组长度size就是0,否则数组长度就不是0。

    • 如果elementData元素是空的,就是第一次添加元素,minCapacity=size+1,其实就是等于1,空的数组没有长度就存放不了,所以就将minCapacity变成10,也就是默认容量的大小。也就是elementData数组的此时需要的最小容量为变10了,此时ensureExplicitCapacity()方法中的minCapacity就是10。到此只是说elementData数组需要的容量至少是10,还没有改变elementData的大小。
    • 如果elementData数组中的元素不是空的,那么它此时需要的最小容量就是原先的数组长度加1,minCapacity代表着elementData中元素增加之后的实际数据个数。
    • 要始终记住minCapacity = size+1,在ensureExplicitCapacity()方法中,首先操作数自增1,再把需要的最小空间容量与数组当前实际长度进行比较:
      • 如果elementData中的元素是空的,它现在需要的容量是10,但是elementData.length为0,所以要扩容。
      • 如果elementData数组中的元素不是空的,若它添加一个元素后需要的容量比原数组长度大,就需要扩容,否则就不需要扩容。
  • ArrayList能自动扩展大小的关键方法就在下面的grow()方法里:

/**
*扩容,保证ArrayList至少能存储minCapacity个元素
*第一次扩容,逻辑为newCapacity=oldCapacity + (oldCapacity >> 1);即在原有容量基础上增加一半。
*第一次扩容后,如果容量还小于minCapacity,就将容量扩充为minCapacity。
*
*/
private void grow(int minCapacity) {
        // overflow-conscious code
    	//获取当前数组的容量
        int oldCapacity = elementData.length;
    	//扩容:新的容量 = 当前容量+(当前容量/2)。即在原有容量基础上增加一半(当前容量的1.5倍)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
    	//如果扩容后容量还是小于想要的最想容量
        if (newCapacity - minCapacity < 0)
            //将扩容后容量再次扩容为想要的最小容量
            newCapacity = minCapacity;
    	//elementData为空数组的时候,length=0,oldCapacity=0,newCapacity,在这里就是真正的初始化elementData的大小:10
    	//如果扩容后的容量大于临界点免责进行大容量分配
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
    	//新的容量确认好了,就copy数组,改变容量大小
    	//copyof(原数组,新的数组长度)
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
//进行容量分配
private static int hugeCapacity(int minCapacity) {
     //minCapacity < 0 抛出异常
     if (minCapacity < 0) // overflow
         throw new OutOfMemoryError();
    //如果想要的容量大于MAX_ARRAY_SIZE,则分配 Integer.MAX_VALUE,反则分配AX_ARRAY_SIZE
     return (minCapacity > MAX_ARRAY_SIZE) ?
         Integer.MAX_VALUE :
         AX_ARRAY_SIZE;
    }

2.LinkedList源码分析

2.1LinkedList继承关系

  • LinkedList(E:支持泛型):它继承了AbstractSequentialList类.
    • 实现接口:List、Deque、Cloneable、java.io.Serializable
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}

2.2数据结构:

//元素个数(节点个数)	
transient int size = 0;
//节点头
transient Node<E> first;
//节点尾
transient Node<E> last;

//私有静态内部类,表示双向链表节点
private static class Node<E> {
        E item;			//元素
        Node<E> next;	//指向后一个节点
        Node<E> prev;	//指向前一个节点

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

2.3 构造方法:

 	public LinkedList() {
    }

    public LinkedList(Collection<? extends E> c) {
        this();
        //添加c到链表中
        addAll(c);
    }

2.4.1 add()增加方法理解:

  • LinkedListCRUD:CRUD代表着的就是增删改查
public boolean add(E e) { //传入参数
        linkLast(e);
        return true;
    }

void linkLast(E e) { //传入的参数
        final Node<E> l = last; //记录尾结点
        final Node<E> newNode = new Node<>(l, e, null);//创建新节点(1.上个节点的地址、2.参数、3.null)
        last = newNode;  //更新尾部节点
        if (l == null)   //判断相等情况下,更新节点头
            first = newNode;
        else   			//否则将新节点的地址赋值给上个节点next位置
            l.next = newNode;
        size++;
        modCount++;
    }
  1. 使用add方法,传入参数。
  2. 记录尾节点。
  3. 创建新节点(节点参数:l:上个节点的地址、e:实参、null)
  4. 更新尾部节点。
  5. 判断列表为空时:更新头节点,first指向新的节点 / 判断列表不为空时:将新节点的地址赋值给上个节点next位置。
2.4.1.11图片分析:

LinkedList图片分析

2.4.2 remove()删除方法

//1.默认的删除方法,它删除的是第一个元素,调用removeFirst()方法
public E remove() {
        return removeFirst();
    }
//2.执行removeFirst()
public E removeFirst() {
        final Node<E> f = first;//f指向first(第一个元素)
        if (f == null)			//判断为空时,抛出异常	
            throw new NoSuchElementException();
        return unlinkFirst(f); //否则使用unlinkFirst方法
    }
private E unlinkFirst(Node<E> f) { 			//unlinkfirst()方法才是关键的地方
        // assert f == first && f != null;
        final E element = f.item;			//将第一个元素赋值给elment
        final Node<E> next = f.next;		//将第一个元素的next指向后面一个元素
        f.item = null;						//将第一个元素赋值为空
        f.next = null; // help GC			//将第一个元素next也赋值为空,让系统以为它是产生的垃圾
        first = next;                       //让first指向后面一位元素
        if (next == null)
            last = null;					//如果为空,last为null
        else
            next.prev = null;				//如果不为空,后一位元素前节点的地址为空,那新的第一位元素和被删除的第一位链表都断了(不指向)
        size--;								//size-1
        modCount++;
        return element;
    }
2.4.2.1图片分析

LinkedList删除元素

3.Vector源码分析

3.1继承关系

  • extends AbstractList:继承 AbstractLis类
  • implements List, RandomAccess, Cloneable, java.io.Serializable:实现接口
public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

3.2成员变量

protected Object[] elementData;//空的数组
protected int elementCount;   //记录数组中元素的个数
protected int capacityIncrement;//扩容大小

3.3构造方法

//有参构造
public Vector(int initialCapacity, int capacityIncrement) {//传入传数
        super();
        if (initialCapacity < 0) //判断传入元素小于0,抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];//创建为一个输入多少参数长度的数组
        this.capacityIncrement = capacityIncrement;//如果不去动,它会一直为0
//有参构造
public Vector(int initialCapacity) {//传入的参数
        this(initialCapacity, 0);//this方法:public Vector(int initialCapacity, int capacityIncrement) {就是上面的有参构造
    }
//无参构造
public Vector() {
        this(10);//这里的this调用的是Vector(int initialCapacity) 方法
    }
public Vector(Collection<? extends E> c) {
        Object[] a = c.toArray();
        elementCount = a.length;
        if (c.getClass() == ArrayList.class) {
            elementData = a;
        } else {
            elementData = Arrays.copyOf(a, elementCount, Object[].class);
        }
    }

3.4解读add方法源码

public synchronized boolean add(E e) {//传入的实参元素
        modCount++;					 //修改次数
        ensureCapacityHelper(elementCount + 1);//
        elementData[elementCount++] = e;
        return true;
    }
private void ensureCapacityHelper(int minCapacity) {//minCapacity:elementCount + 1,
        if (minCapacity - elementData.length > 0) //当判断为true时,使用grow方法(扩容),反则不扩容
            grow(minCapacity);
    }

  
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private void grow(int minCapacity) {
        
        int oldCapacity = elementData.length; //数组当前大小赋值给old
        //old+(这边是一个三元运算为true时,取capacityIncrement:0;false时取,oldCapacity)
        //当三元运算为false时,就是oldCapacity+oldCapacity,相当于2倍扩容
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?       
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)//当这边的判断成立时,将minCapacity的值赋值给newCapacity
            							//false时,直接执行Arrays.copyOf 按2倍扩容
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);//原数组,扩容的大小(按2倍扩容)
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值