Java 常用 List 集合使用场景分析,springboot 项目实战
技术:ArrayList,LinkedList,Vector,CopyOnWriteArrayList
说明:本章基于 jdk1.8,github 上有 ArrayList,LinkedList 的简单源码代码
源码:https://github.com/ITDragonBlog/daydayup/tree/master/Java/collection-stu
知识预览
ArrayList?: 基于数组实现的非线程安全的集合。查询元素快,插入,删除中间元素慢。
LinkedList?: 基于链表实现的非线程安全的集合。查询元素慢,插入,删除中间元素快。
Vector?: 基于数组实现的线程安全的集合。线程同步(方法被 synchronized 修饰),性能比 ArrayList 差。
CopyOnWriteArrayList?: 基于数组实现的线程安全的写时复制集合。线程安全(ReentrantLock 加锁),性能比 Vector 高,适合读多写少的场景。
ArrayList 和 LinkedList 读写快慢的本质
ArrayList : 查询数据快,是因为数组可以通过下标直接找到元素。 写数据慢有两个原因:一是数组复制过程需要时间,二是扩容需要实例化新数组也需要时间。
LinkedList : 查询数据慢,是因为链表需要遍历每个元素直到找到为止。 写数据快有一个原因:除了实例化对象需要时间外,只需要修改指针即可完成添加和删除元素。
本章会通过源码分析,验证上面的说法。
注:这里的块和慢是相对的。并不是 LinkedList 的插入和删除就一定比 ArrayList 快。明白其快慢的本质:ArrayList 快在定位,慢在数组复制。LinkedList 慢在定位,快在指针修改。
ArrayList
ArrayList 是基于动态数组实现的非线程安全的集合。当底层数组满的情况下还在继续添加的元素时,ArrayList 则会执行扩容机制扩大其数组长度。ArrayList 查询速度非常快,使得它在实际开发中被广泛使用。美中不足的是插入和删除元素较慢,同时它并不是线程安全的。
我们可以从源码中找到答案
// 查询元素 public E get(int index) { rangeCheck(index); // 检查是否越界 return elementData(index); } // 顺序添加元素 public boolean add(E e) { ensureCapacityInternal(size + 1); // 扩容机制 elementData[size++] = e; return true; } // 从数组中间添加元素 public void add(int index, E element) { rangeCheckForAdd(index); // 数组下标越界检查 ensureCapacityInternal(size + 1); // 扩容机制 System.arraycopy(elementData, index, elementData, index + 1, size - index); // 复制数组 elementData[index] = element; // 替换元素 size++; } // 从数组中删除元素 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 }
从源码中可以得知,
ArrayList 在执行查询操作时:
第一步:先判断下标是否越界。
第二步:然后在直接通过下标从数组中返回元素。
ArrayList 在执行顺序添加操作时:
第一步:通过扩容机制判断原数组是否还有空间,若没有则重新实例化一个空间更大的新数组,把旧数组的数据拷贝到新数组中。
第二步:在新数组的最后一位元素添加值。
ArrayList 在执行中间插入操作时:
第一步:先判断下标是否越界。
第二步:扩容。
第三步:若插入的下标为 i,则通过复制数组的方式将 i 后面的所有元素,往后移一位。
第四步:新数据替换下标为 i 的旧元素。
删除也是一样:只是数组往前移了一位,最后一个元素设置为 null,等待 JVM 垃圾回收。
从上面的源码分析,我们可以得到一个结论和一个疑问。
结论是:ArrayList 快在下标定位,慢在数组复制。
疑问是:能否将每次扩容的长度设置大点,减少扩容的次数,从而提高效率?其实每次扩容的长度大小是很有讲究的。若扩容的长度太大,会造成大量的闲置空间;若扩容的长度太小,会造成频发的扩容(数组复制),效率更低。
LinkedList
LinkedList 是基于双向链表实
《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
现的非线程安全的集合,它是一个链表结构,不能像数组一样随机访问,必须是每个元素依次遍历直到找到元素为止。其结构的特殊性导致它查询数据慢。
我们可以从源码中找到答案
// 查询元素 public E get(int index) { checkElementIndex(index); // 检查是否越界 return node(index).item; } 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; } } // 插入元素 public void add(int index, E element) { checkPositionIndex(index); // 检查是否越界 if (index == size) // 在链表末尾添加 linkLast(element); else // 在链表中间添加 linkBefore(element, 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 在执行查询操作时:
第一步:先判断元素是靠近头部,还是靠近尾部。
第二步:若靠近头部,则从头部开始依次查询判断。和 ArrayList 的elementData(index)
相比当然是慢了很多。
LinkedList 在插入元素的思路:
第一步:判断插入元素的位置是链表的尾部,还是中间。
第二步:若在链表尾部添加元素,直接将尾节点的下一个指针指向新增节点。
第三步:若在链表中间添加元素,先判断插入的位置是否为首节点,是则将首节点的上一个指针指向新增节点。否则先获取当前节点的上一个节点(简称 A),并将 A 节点的下一个指针指向新增节点,然后新增节点的下一个指针指向当前节点。
Vector
Vector 的数据结构和使用方法与 ArrayList 差不多。最大的不同就是 Vector 是线程安全的。从下面的源码可以看出,几乎所有的对数据操作的方法都被 synchronized 关键字修饰。synchronized 是线程同步的,当一个线程已经获得 Vector 对象的锁时,其他线程必须等待直到该锁被释放。从这里就可以得知 Vector 的性能要比 ArrayList 低。
若想要一个高性能,又是线程安全的 ArrayList,可以使用Collections.synchronizedList(list);
方法或者使用 CopyOnWriteArrayList 集合
最后
小编在这里分享些我自己平时的学习资料,由于篇幅限制,pdf 文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
开源分享:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】
程序员代码面试指南 IT 名企算法与数据结构题目最优解
这是” 本程序员面试宝典!书中对 IT 名企代码面试各类题目的最优解进行了总结,并提供了相关代码实现。针对当前程序员面试缺乏权威题目汇总这一-痛点, 本书选取将近 200 道真实出现过的经典代码面试题,帮助广“大程序员的面试准备做到万无一失。 “刷”完本书后,你就是“题王”!

《TCP-IP 协议组(第 4 版)》
本书是介绍 TCP/IP 协议族的经典图书的最新版本。本书自第 1 版出版以来,就广受读者欢迎。
本书最新版进行」护元,以体境计算机网络技不的最新发展,全书古有七大部分共 30 草和 7 个附录:第一部分介绍一些基本概念和基础底层技术:第二部分介绍网络层协议:第三部分介绍运输层协议;第四部分介绍应用层协议:第五部分介绍下一代协议,即 IPv6 协议:第六部分介绍网络安全问题:第七部分给出了 7 个附录。

Java 开发手册(嵩山版)
这个不用多说了,阿里的开发手册,每次更新我都会看,这是 8 月初最新更新的**(嵩山版)**

MySQL 8 从入门到精通
本书主要内容包括 MySQL 的安装与配置、数据库的创建、数据表的创建、数据类型和运算符、MySQL 函数、查询数据、数据表的操作(插入、更新与删除数据)、索引、存储过程和函数、视图、触发器、用户管理、数据备份与还原、MySQL 日志、性能优化、MySQL Repl ication、MySQL Workbench、 MySQL Utilities、 MySQL Proxy、PHP 操作 MySQL 数据库和 PDO 数据库抽象类库等。最后通过 3 个综合案例的数据库设计,进步讲述 MySQL 在实际工作中的应用。

Spring5 高级编程(第 5 版)
本书涵盖 Spring 5 的所有内容,如果想要充分利用这一领先的企业级 Java 应用程序开发框架的强大功能,本书是最全面的 Spring 参考和实用指南。
本书第 5 版涵盖核心的 Spring 及其与其他领先的 Java 技术(比如 Hibemate JPA 2.Tls、Thymeleaf 和 WebSocket)的集成。本书的重点是介绍如何使用 Java 配置类、lambda 表达式、Spring Boot 以及反应式编程。同时,将与企业级应用程序开发人员分享一些见解和实际经验,包括远程处理、事务、Web 和表示层,等等。

JAVA 核心知识点+1000 道 互联网 Java 工程师面试题


企业 IT 架构转型之道 阿里巴巴中台战略思想与架构实战
本书讲述了阿里巴巴的技术发展史,同时也是-部互联网技 术架构的实践与发展史。

评论