ArrayList和LinkedList源码分析

本文对ArrayList和LinkedList进行源码分析。ArrayList底层是动态数组,LinkedList底层是双向列表。文中详细介绍了两者add、remove、get、contain等方法的实现原理,并对比了在末尾删除、尾节点添加、中间删除、随机获取、中间添加等操作时的性能差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ArrayList和LinkedList源码分析: 

package easyExcel;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.LinkedList;

public class App 
{
    public static void main( String[] args ) throws FileNotFoundException {
        //从构造函数可以看出 ArrayList 底层是由数组构成的

        //LinkedList 源码分析

        /**
           无参构造方法
           public ArrayList() {
             super();
             this.elementData = EMPTY_ELEMENTDATA; //初始化
             }

         Object[] elementData //保存数组元素
         Object[] EMPTY_ELEMENTDATA = {};
         
         
         */
        ArrayList list = new ArrayList<>();

        //添加数据

        /**

              public boolean add(E e) {
                 ensureCapacityInternal(size + 1);  // 判断是否扩容 以及容器的初始化大小
                 elementData[size++] = e; //存储元素
                 return true;
                 }


              private static final int DEFAULT_CAPACITY = 10;//容器默认大小

              private void ensureCapacityInternal(int minCapacity) {
                 if (elementData == EMPTY_ELEMENTDATA) { //如果数组为空数组
                    minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //取最大值 设置数组容量
                 }

                 ensureExplicitCapacity(minCapacity);
                 }

         private void ensureExplicitCapacity(int minCapacity) {
             modCount++;

             if (minCapacity - elementData.length > 0) //判断是否扩容 若为空数组 满足 minCapacity - elementData.length > 0 在超出数组容量的时候也满足
             grow(minCapacity);
         }

         //扩容 机制
         private void grow(int minCapacity) {
             // overflow-conscious code
             int oldCapacity = elementData.length; //原始数据的长度
             int newCapacity = oldCapacity + (oldCapacity >> 1); //扩容为1.5倍的长度
             if (newCapacity - minCapacity < 0) //新的容器容量 小于 minCapacity容量 ----对应的数组的初始化
             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); //实际扩容操作
         }

         */
        list.add("1");

        //根据索引获取数据

        /**
             public E get(int index) {
             rangeCheck(index); //索引校验

             return elementData(index);
             }

             E elementData(int index) {
             return (E) elementData[index]; //根据数组的下标获取数据
             }

         */
        list.get(1);


         // 在中间插入元素

        /**
             public void add(int index, E element) {
             rangeCheckForAdd(index); //校验索引

             ensureCapacityInternal(size + 1);  // 判断是否需要扩容 见上

          arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length);
          @param      src      the source array. 源数组
          @param      srcPos   starting position in the source array. 开始的位置
          @param      dest     the destination array. 目标数组
          @param      destPos  starting position in the destination data. 在目标数组数组什么位置开始复制
          @param      length   the number of array elements to be copied. 复制的长度

             System.arraycopy(elementData, index, elementData, index + 1,
             size - index); // 次操作 把index位置空出来 导致index开始所有的数据都要向后移动一位
             elementData[index] = element; //将值填充进去
             size++; //数组大小加1
             }
         */
        list.add(1,"2");

        //根据索引移除

        /**
             public E remove(int index) {
             rangeCheck(index); //索引校验

             modCount++;
             E oldValue = elementData(index); //找到对应的元素

             int numMoved = size - index - 1; //需要移动的数据
             if (numMoved > 0)
             System.arraycopy(elementData, index+1, elementData, index,
             numMoved);//将index后面的元素逐个向前移动一位
             elementData[--size] = null; // clear to let GC do its work
             return oldValue;
             }
         */

        list.remove(1);

        //根据元素移除

        /**
             public boolean remove(Object o) {
             if (o == null) { //o为空
             for (int index = 0; index < size; index++)
             if (elementData[index] == null) { //找到所有为null的元素
             fastRemove(index); //删除元素
             return true;
             }
             } else { // o不为null
             for (int index = 0; index < size; index++) //遍历所有的元素找出
             if (o.equals(elementData[index])) { //找出 元素等于o的o.equals(elementData[index])
             fastRemove(index); //删除元素
             return true;
             }
             }
             return false;
             }


            private void fastRemove(int index) { //根据索引删除
                modCount++;
                int numMoved = size - index - 1;
                if (numMoved > 0)
                    System.arraycopy(elementData, index+1, elementData, index,
                            numMoved);
                elementData[--size] = null; // clear to let GC do its work and size-1
            }
         */
        list.remove("3");

        //是否包含

        /**
             public boolean contains(Object o) {
                return indexOf(o) >= 0;
             }

         public int indexOf(Object o) {
             if (o == null) { //o为null
                for (int i = 0; i < size; i++) //循环
             if (elementData[i]==null) //判断有一个为null 怎返回
                return i;
             } else { //o不为null
                for (int i = 0; i < size; i++) //循环
                    if (o.equals(elementData[i])) // 找出与o相等的元素 然后返回
                        return i;
             }
             return -1;
         }

         */
        list.contains("3");




        //LinkedList 源码分析

        LinkedList linkedList = new LinkedList();

        //添加数据

        /**
              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;
         }
         }
         
          在LinkedList内部中
         
          transient int size = 0;  元素个数

          transient Node<E> first; 首节点

          transient Node<E> last; 尾节点
         
         */
        //在linkedList 内部有一个内部类 Node 节点
        //节点保存的是当前元素,以及前一个节点和后一个节点
        /**
              void linkLast(E e) {
                 final Node<E> l = last;
                 final Node<E> newNode = new Node<>(l, e, null);
                 last = newNode;
                 if (l == null)
                 first = newNode;
                 else
                 l.next = newNode;
                 size++;
                 modCount++;
                }
         
          添加节点的时候
          ① Links e as last element 将元素作为尾节点添加 将该元素封装成一个节点 并指向上一个节点
          ②再判断尾节点是否为null 为null 将该元素设置为首节点
          ③否则将该节点作为原尾节点的下一节点 形成一个双向链表
         */

        linkedList.add(1);


        //按索引获取数据

        /**
         
              Node<E> node(int index) {
                 if (index < (size >> 1)) {
                 Node<E> x = first;
                 for (int i = 0; i < index; i++)
                 x = x.next;
                 return x;
                 } else {
                 Node<E> x = last;
                 for (int i = size - 1; i > index; i--)
                 x = x.prev;
                 return x;
                 }
                 }
         
          ①二分法查找方 index < (size >> 1)
          ②循环遍历 找到对应索引的元素节点
         
         */
        linkedList.get(1);

        //按索引添加数据

        /**
          向链表中间插入元素
         
         
              public void add(int index, E element) {
                 checkPositionIndex(index);
                 if (index == size)
                 linkLast(element);
                 else
                 linkBefore(element, node(index));
                 }
         
         
          ①判断插入的位置 是否在最后位置插入
          ②就添加到将该元素放到尾节点
          ③否则 在中间插入
          linkBefore(element, node(index)); node(index)获取该索引下的节点
         
         
              void linkBefore(E e, Node<E> succ) {
                 final Node<E> pred = succ.prev;   索引前置节点 本身成为插入元素的后置节点
                 final Node<E> newNode = new Node<>(pred, e, succ);
                 succ.prev = newNode;
                 if (pred == null)
                 first = newNode;
                 else
                 pred.next = newNode;
                 size++;
                 modCount++;
                 }
         
          封装节点设置该元素的前置节点和后置节点
          并且
          将对应索引节点的前一个节点 设置成这个新节点
          对应索引的前一个节点的下一个节点 设置成这个新节点
         
         */

        linkedList.add(1,99);


        //按索引删除数据

        /**
         
          public E remove(int index) {
         checkElementIndex(index);
         return unlink(node(index));
         }
         
         
         
          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; //前一个节点 前置节点

             if (prev == null) {
             first = next;
             } else {
             prev.next = next; //重组节点 前置节点的 下一个节点 指向 后置节点
             x.prev = null;
             }

             if (next == null) {
             last = prev;
             } else {
             next.prev = prev; //重组节点 后置节点 前一个节点 指向 前置节点的
             x.next = null;
             }

             x.item = null;
             size--;
             modCount++;
             return element;
             }
         
         
         */
        linkedList.remove(1);

        // 判断元素是否存在

        /**
         public int indexOf(Object o) {
             int index = 0;
             if (o == null) {
                for (Node<E> x = first; x != null; x = x.next) { //链表遍历
                    if (x.item == null) //找出元素为null的
                        return index;
                   index++;
                }
             } else {
             for (Node<E> x = first; x != null; x = x.next) { //链表遍历
                 if (o.equals(x.item)) //找出相等的元素
                    return index;
                 index++;
             }
             }
             return -1;
         }
         */
        linkedList.contains(1);

    }
}

总结:

ArrayList:底层是由动态数组组成。

LinkedList:底层是双向列表构成,每一个节点(头尾节点除外)都有指向前一个节点,和下一个节点的引用。

add方法:

ArrayList:若是无参构造方法,会先初始化一个空的数组,添加的时候初始换数组大小默认数组长度为10。

LinkedList:是构建一个Node节点,作为尾节点添加到链表中去(若尾节点为null,则头尾节点为同一个节点)会建立该节点的前节点的引用,以及原尾节点下一个节点的引用关系。

and(int index)中间添加元素

ArrayList:先判断是否扩容,然后将index后面的元素整体后移一位后,然后将值复制的index位置

LinkedList:现根据索引找到该索引的节点A,获取他前一个节点B,构建新的元素C,C节点的前一个节点指向B,B的下一个节点指向C,C的下一个节点指向B,B的前一个节点指向C

remove方法 根据索引删除

ArrayList:会根据数组下表找到该节点Node,然后做赋值操作,将index后面的元素向前移动一个位置。

LinkedList:先通过二分法找到该节点(Node(index)方法),然后找到该节点的前一个节点A和后一个节点B,将A节点的下一个节点的引用指向B,B的前一个节点的引用指向A完成删除。

get方法 索引

ArrayList:根据数组下标直接获取

LinkedList:现根据二分法(size>>1)判断index所在的位置,然后遍历(0到index或size-1到index)找到对应的节点

contain方法

ArrayList:遍历数组,判断是否有元素等于指定的元素

LinkedList:遍历链表 找到是由有节点元素等于指定的元素

由上可知:,末尾删除元素ArrayList直接删除即可,LinkedList需要分配索引;尾节点添加元素(不需要扩容),ArrayList会直接存储元素,LinkedList需要分配索引;中间删除元素时LinkedList只需重新分配引用即可,ArrayList(不需要扩容,需要的话会扩容)索引后面的元素都要向前面移动一位;随机获取元素的时候ArrayList根据下标直接获取,LinkedList使用二分法遍历获取;中间添加元素LinkedList只需要重新分配引用即可,ArrayList 则要从index位置向后移动一位给插入的元素;尾节点插入元素ArrayList较快,LinkedList需要建立引用关系;ArrayList由于采用的是数组会浪费一定的空间(数组不一定存储满)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值