《别看了,你学不会的》——ArrayList与LinkList源码解析

本文深入解析了ArrayList和LinkedList的源码,包括ArrayList的默认容量、元素添加与删除的逻辑,以及LinkedList的节点操作。ArrayList在添加元素时会根据需要动态扩容,首次扩容为10个元素。删除元素时,ArrayList会移动后续元素并调整容量。LinkedList通过节点链接实现添加和删除操作,高效地处理链表结构中的元素增删。
摘要由CSDN通过智能技术生成

ArrayList源码解析

ArrayList默认的容量为10个Object元素
private static final int DEFAULT_CAPACITY = 10;
ArrayList中包含的元素数量
 private int size;
elementData被transient修饰,ArrayList在序列化的时候会调用writeObject,直接将size和element写入ObjectOutputStream;反序列化时调用readObject,从ObjectInputStream获取size和element,再恢复到elementData
为什么不直接用elementData来序列化?原因在于elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上述的方式来实现序列化时,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而提高效率,节省空间和时间
 transient Object[] elementData; // non-private to simplify nested class access

初始化ArrayList实例,则elementData={}

 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; // Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    }

新增元素
List list = new ArrayList();
list.add(“a1”);

第一次新增元素e=“a1”

 public boolean add(E e) {
        /** 确定是否需要扩容,如果需要,则进行扩容操作*/
        ensureCapacityInternal(size + 1);  // Increments modCount!!

        // eg1:size=0,elementData[0]="a1",然后a自增为1
        elementData[size++] = e;
        return true;
    }

计算ArrayList的容量
如果elementData数组中没有已存储的元素,则返回默认值10,否则返回minCapacity

elementData:底层存储ArrayList元素的数组
minCapacity:ArrayList中的元素个数
size:

// eg1:第一次新增元素,elementData={} minCapacity=1
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // eg1:满足if判断,DEFAULT_CAPACITY=10
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    // eg1:第一次新增元素,所以size=0,则:minCapacity=size+1=1
    private void ensureCapacityInternal(int minCapacity) {
        // eg1:第一次新增元素,calculateCapacity方法返回值为DEFAULT_CAPACITY=10
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
注意:ArrayList在刚开始初始化的时候,容量是0;只有在放入第1个元素的时候,容量才是10。背后的逻辑是,创建ArrayList之后并不知道是否会使用,如果初始化就分配容量,会造成对内存的浪费;所以只有在放入元素的时候才会分配内存

明确ArrayList的容量
minCapacity:ArrayList所需的最小容量

    private void ensureExplicitCapacity(int minCapacity) {
        // eg1: modCount++后,modCount=1
        modCount++;

        /** 如果所需的最小容量大于elementData数组的容量,则进行扩容操作 */
        if (minCapacity - elementData.length > 0) { // eg1:10-0=10,满足扩容需求
            // eg1:minCapacity=10
            grow(minCapacity);
        }
    }

扩容:新增oldCapacity的一半整数长度作为newCapacity的额外增长长度

// eg1:第一次新增元素,minCapacity=10,即:需要将elementData的0长度扩容为10长度。
    private void grow(int minCapacity) {

        /** 原有数组elementData的长度*/
        int oldCapacity = elementData.length; // eg1:oldCapacity=0
        /** 新增oldCapacity的一半整数长度作为newCapacity的额外增长长度 */
        int newCapacity = oldCapacity + (oldCapacity >> 1); // eg1:newCapacity=0+(0>>1)=0

        /** 新的长度newCapacity依然无法满足需要的最小扩容量minCapacity,则新的扩容长度为minCapacity */
        if (newCapacity - minCapacity < 0) {
            // eg1:newCapacity=10
            newCapacity = minCapacity;
        }

        /** 新的扩容长度newCapacity超出了最大的数组长度MAX_ARRAY_SIZE huge:巨大的 */
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            newCapacity = hugeCapacity(minCapacity);
        }

        /** 扩展数组长度为newCapacity,并且将旧数组中的元素赋值到新的数组中 */
        // eg1:newCapacity=10, 扩容elementData的length=10
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

删除元素

public E remove(int index) {
        /** 校验传入的参数index是否超出了数组的最大下标,如果超出,则抛出:IndexOutOfBoundsException异常*/
        rangeCheck(index);

        /** 集合的修改次数加1 */
        modCount++;

        // eg1:String oldValue="a1"
        /** 获得index下标对应的旧值oldValue */
        E oldValue = elementData(index);

        // eg1:numMoved=4-0-1=3
        /** 获得需要移动元素的个数 */
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            /**
             * {"a1","a2","a3","a4"}
             *
             * 假设删除"a2",那么index=1, numMoved=2, 那么下面的语句执行的就是:
             * 将第一个入参的elementData从第2位"a3"(index+1)开始复制2位(numMoved)即:"a3","a4";
             * 复制到第三个入参elementData的第1位(index),即elementData变成 {"a1","a3","a4","a4"}
             */
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        /** 通知jvm将之前的最后一位元素进行垃圾回收 */
        elementData[--size] = null; // clear to let GC do its work

        /** 返回已被删除的元素 */
        return oldValue;
    }

LinkList源码解析

结点
transient Node<E> first;
// 指向最后一个结点
transient Node<E> last;

将新添加的元素e作为链表的最后一个元素,并维护进去

void linkLast(E e) {
        final Node<E> l = last;

        // eg1: newNode    null<--"a1"-->null
        /** 创建一个e的Node节点,前置指向原last节点,后置指向null */
        final Node<E> newNode = new Node<>(l, e, null);

        /** 将newNode节点赋值为last节点 */
        last = newNode;

        // eg1: l=null
        if (l == null) {
            /** 如果是第一个添加的元素,则first指针指向该结点*/
            first = newNode; // eg1: first指向newNode
        } else {
            /** 如果不是第一个添加进来的元素,则更新l的后置结点指向新添加的元素结点newNode*/
            l.next = newNode;
        }
        size++;
        modCount++;
    }

Node传的值

    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;
        }
    }

从链表中删除x结点的链接

E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        /** x.prev为null,表示x结点为第一个元素*/
        if (prev == null) {
            first = next; // 更新first头指针为x结点的后置结点
        } else {
            prev.next = next; // 将x的前置结点与x的后置结点相连接
            x.prev = null; // 断开x的前置指针
        }

        /** x.next为null,表示x结点为最后一个元素*/
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev; // 将x的后置结点与x的前置结点相连接
            x.next = null; // 断开x的后置指针
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

删除元素

public E remove(int index) {
        /** 校验传入的参数index是否超出了数组的最大下标且下标不为负数,如果超出,则抛出:IndexOutOfBoundsException异常*/
        checkElementIndex(index);
        // eg1:node(index)返回需要删除的结点,即:"a1"
        return unlink(node(index)); /** 断开待删除结点的链接 */
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值