List接口中的ArrayList与LinkedList源码解读

ArrayList

ArrayList的继承实现关系图
在这里插入图片描述

1、ArrayList中的属性

ArrayList 底层数据结构就是⼀个 Object[] 数组,根据扩容机制可实现动态增长,因此也叫动态数组。
在这里插入图片描述
在这里插入图片描述

    // ArrayList集合默认容量-->数组长度10
    private static final int DEFAULT_CAPACITY = 10;

    // 空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};

    // 空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    // ArrayList底层存储数据的结构Object[]数组
    transient Object[] elementData; // non-private to simplify nested class access

    // size是ArrayList集合中的数据个数
    private int size;
    

2、ArrayList中的构造方法

当调用空参构造方法实例化一个ArrayList时、第⼀次添加元素(调⽤ add() ⽅法)时会根据扩容机制初始化其容量为10 。

在这里插入图片描述

// 空参构造方法
    public ArrayList() {
        // 给当前elementData赋值空数组,创建数组,但是长度为0
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    // 带参构造方法,initialCapacity初始容量,是给数组开辟空间
    public ArrayList(int initialCapacity) {
        // initialCapacity大于0
        if (initialCapacity > 0) {
            // 创建数组并开辟initialCapacity容量的空间大小
            this.elementData = new Object[initialCapacity];
        // initialCapacity等于0
        } else if (initialCapacity == 0) {
            // 给当前elementData赋值空数组,创建数组,但是长度为0
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            // initialCapacity小于0则抛出异常
            throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
        }
    }
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();//将集合转化为数组
        if ((size = elementData.length) != 0) {
            // 判断数组的Class对象等不等于object[]的Class对象
            if (elementData.getClass() != Object[].class)
            	//进行数组的复制
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }    

可以创建指定大小的数组,因为ArrayList 容量使⽤完后,会“⾃动”创建容量更⼤的数组,并将原数组中所有元素拷⻉过去,这会导致效率过低,可以使⽤构造⽅法 ArrayList (int capacity) 或 ensureCapacity(int capacity) 提供⼀个初始化容量,避免刚开始就⼀直扩容,造成效率较低

3、ArrayList的扩容机制

// 添加数据方法
    public boolean add(E e) {
        // 确保内部容量-->确保数组的长度是否可以继续添加数据
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 向数组中添加数据
        elementData[size++] = e;// size++加加在后先赋值再运算
        // 默认返回true
        return true;
    }
    
    // 确保内部容量方法
    private void ensureCapacityInternal(int minCapacity) {
        // 判断elementData是否是空数组
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 如果是则更改最小容量,使用Math数学类的max方法获取两个值得最大值
            //因为一开始是空数组,所以minCapacity是DEFAULT_CAPACITY.
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        // 确保显式容量方法,将DEFAULT_CAPACITY传进去
        ensureExplicitCapacity(minCapacity);
    }
    
    // 确保显式容量方法
    private void ensureExplicitCapacity(int minCapacity) {
        // 修改次数
        modCount++;

        // 判断最小容量减去数组容量是否大于0如果大于则代表数组不能再添加数据了
        // DEFAULT_CAPACITY-elementData.length>0
        if (minCapacity - elementData.length > 0)
            // 扩容方法,传进去的是DEFAULT_CAPACITY
            grow(minCapacity);
    }
    
    // 数组的最大容量-->2147483647-8=2147483639
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    // 扩容方法
    private void grow(int minCapacity) {
        // 获取数组长度赋值给oldCapacity旧容量
        int oldCapacity = elementData.length;
        // 计算新容量-->使用旧容量+旧容量右移1位(等同于除于2)-->原容量的1.5倍
        //初始ArrayList的oldCapacity 为0
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 扩容后的新容量还比需要的最小容量小,那么将minCapacity赋给newCapacity 
        //newCapacity - DEFAULT_CAPACITY < 0,newCapacity = DEFAULT_CAPACITY 
        if (newCapacity - minCapacity < 0)
            // 将最小容量赋值给新容量
            newCapacity = minCapacity;
        // 最大长度2147483639
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            // 如果超出最大长度2147483639则会用另外的方式扩容
            newCapacity = hugeCapacity(minCapacity);
        // 最终使用Arrays的copyOf方法扩容,创建新数组并将旧数组数据赋值给新数组,以及替换elementData数据
        //经过第一次add()方法,ArrayList的容量变为10.
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
}

ArrayList优点:

  • 存取有序、可重复可为null,动态数组,可改变数组大小
  • 查询效率⾼

ArrayList缺点:

  • 扩容会造成效率较低(可以通过指定初始化容量,在⼀定程度上对其进⾏改࠺)
  • 另外数组⽆法存储⼤数据量(因为很ᵙ找到⼀块很⼤的连续的内存空间)
  • 向 ArrayList 中添加/删除元素(add(int index)),需要移动元素,效率较低

LinkedList

LinkedList继承实现关系图
在这里插入图片描述

1、LinkedList中的属性

LinkedList中就三个有用属性:size、first、last

在这里插入图片描述
LinkedList 底层数据结构是⼀个双向链表,优点是增/删效率⾼,缺点是查询效率较低。在这里插入图片描述

Linked中的构造方法

就两个构造方法,一个空参、一个有参。

在这里插入图片描述linkedlist中的add方法与get方法

    // 添加数据方法
    public boolean add(E e) {
        // 向链表中最后一个添加数据
        linkLast(e);
        // 始终返回true
        return true;
    }

    // 向链表中最后一个添加数据
    void linkLast(E e) {
        // 将LinkedList集合中的最后一个节点对象取出赋值给临时节点对象l
        final Node<E> l = last;
        // 创建新节点对象参数是(prev=l,item=e,next=null)
        final Node<E> newNode = new Node<>(l, e, null);
        // 将新创建的节点对象赋值给last,只要创建一个节点对象那么他肯定是最后一个节点(尾节点)
        last = newNode;
        // 判断原尾结点是否为null
        if (l == null)
            // 将新建节点对象赋值给first第一个节点对象(首节点)
            first = newNode;
        else
            // 将l尾结点的next指向新建的节点对象(指向下一个节点对象)
            l.next = newNode;
        // 每次添加数据时size自增一,用于记录LinkedList集合的数据有多少
        size++;
        // 统计次数
        modCount++;
    }

    // 通过下标索引获取元素
    public E get(int index) {
        // 检查元素下标索引是否合法
        checkElementIndex(index);
        // node方法就是获取下标索引数据
        return node(index).item;
    }

LinkedList底层使用链表结构(双链表)实现,双链表的有点是插入和删除数据速度快,但是查询速度慢(不可以指定下标索引查找)
双链表中所有的数据只要是查询就必须从头到尾的遍历一遍,所以会导致速度非常慢
LinkedList链表结构

总结

LinkedList与ArrayList区别:
相同点:

  1. 二者都是 List 接口的实现类,具有元素可重复,有序(存取顺序)特点;
  2. 二者都是线程不安全,效率高;

不同点:

  1. 数据结构:ArrayList底层数据结构是动态数组,LinkedList底层数据结构是双向链表;
  2. 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
  3. 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高, 因为 ArrayList 增删操作要影响数组内的其他数据的下标。

综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值