ArrayList和LinkedList实现原理

一、ArrayList实现原理

        ArrayList就是动态数组,ArrayList并不是线程安全的,允许 null 的存在,是有序的,初始容量大小为10,当超过容量后,会进行扩容,以原来容量的1.5倍来扩容,如之前的容量为10,下次扩容扩大到10 + 5,ArrayList的最大长度为 2^32 .当删除数组中间的元素,后面的元素都会向前移动。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,这种操作的代价是很高的,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素 前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。

        注意:注释是说构造一个容量大小为 10 的空的 list 集合,但构造函数了只是给 elementData 赋值了一个空的数组,其实是在第一次添加元素时容量扩大至 10 的。

二、LinkedList底层实现和原理

        LinkedList类是List接口的实现类,它是一个集合,可以根据索引来随机的访问集合中的元素,还实现了Deque接口,它还是一个队列,可以被当成双端队列来使用。虽然LinkedList是一个List集合,但是它的实现方式和ArrayList是完全不同的,ArrayList的底层是通过一个动态的Object[]数组来实现的,而LinkedList的底层是通过链表来实现的,因此它的随机访问速度是比较差的,但是它的删除,插入操作会很快。

实现接口

Serializable, Cloneable, Iterable<E>, Collection<E>, Deque<E>, List<E>, Queue<E>

源码解析:

        LinkedList是通过双向链表去实现的,既然是链表实现那么它的随机访问效率比ArrayList要低,顺序访问的效率要比较的高。每个节点都有一个前驱(之前前面节点的指针)和一个后继(指向后面节点的指针),效果如下图:

public class LinkedList<E>extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    transient int size = 0;   //LinkedList中存放的元素个数

    transient Node<E> first;  //头节点
    
    transient Node<E> last;   //尾节点

    //构造方法,创建一个空的列表
    public LinkedList() {
    }

    //将一个指定的集合添加到LinkedList中,先完成初始化,在调用添加操作
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
    。。。。。。
}

构造方法:

LinkedList()

LinkedList(Collection c)

        LinkedList没有长度的概念,所以不存在容量不足的问题,也不存在扩容的问题,因此不需要提供初始化大小的构造方法,因此值提供了两个方法,一个是无参构造方法,初始一个LinkedList对象,和将指定的集合元素转化为LinkedList构造方法。

添加方法:

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);
     last = newNode;
     if (l == null)
         first = newNode;
     else
          l.next = newNode;
     size++;
     modCount++;
}

添加方法默认是添加到LinkedList的尾部。

删除方法:

        先循环遍历列表,找到item == o 的节点,在调用unlink()方法删除。

总结:

        LinkedList是一个功能很强大的类,可以被当作List集合,双端队列和栈来使用。LinkedList底层使用链表来保存集合中的元素,因此随机访问的性能较差,但是插入删除时性能非常的出色。

List实现类的使用场景:

  • ArrayList,底层采用数组实现,如果需要遍历集合元素,应该使用随机访问的方式,对于LinkedList集合应该采用迭代器的方式
  • 如果需要经常的插入。删除操作可以考虑使用LinkedList集合
  • 如果有多个线程需要同时访问List集合中的元素,开发者可以考虑使用Collections将集合包装成线程安全的集合。

面试题:快速失败(fail-fast)和安全失败(fail-safe)了解吗?

        快速失败(fail—fast):快速失败是Java集合的一种错误检测机制

        在用迭代器遍历一个集合对象时,如果线程A遍历过程中,线程B对集合对象的内容进行了修改(增加、删除、修改),则会抛出ConcurrentModificationException。

        原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个modCount变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext/next遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

        注意:这里异常的抛出条件是检测到modCount!=expectedmodCount这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。

        场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改),比如ArrayList类。

        安全失败(fail—safe):采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

        原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发ConcurrentModificationException。

        缺点:基于拷贝内容的优点是避免了ConcurrentModificationException,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

        场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改,比如CopyOnWriteArrayList类。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
抱歉,我无法提供思维导图。但我可以为你总结arraylistlinkedlist的一些关键特点。 ArrayListLinkedList都是Java中的集合类,用于存储和操作一组对象。然而,它们的实现方式和性能特点有所不同。 ArrayList的底层使用Object[]数组来实现,它可以根据索引随机访问元素。由于数组的连续内存分配,ArrayList在随机访问和尾部插入元素方面具有很好的性能。然而,插入和删除元素时,ArrayList需要移动其他元素以保持连续的内存空间,因此性能较差。 LinkedList的底层使用链表来实现,每个元素都包含一个指向前一个和后一个元素的引用。由于链表的非连续内存分配,LinkedList在插入和删除元素时具有较好的性能,因为只需要修改前后元素的引用。然而,由于需要遍历链表来访问元素,LinkedList在随机访问方面性能较差。 综上所述,ArrayList适用于需要频繁随机访问元素的场景,而LinkedList适用于需要频繁插入和删除元素的场景。选择哪种集合类取决于你的具体需求和对性能的要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Java基础(一)-ArrayListLinkedList性能对比与原因探索](https://blog.csdn.net/weixin_44212764/article/details/123573370)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [ArrayListLinkedList实现原理](https://blog.csdn.net/m0_67402564/article/details/123499751)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的IT蜗牛

更美口味,打赏人生

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值