从源码分析常见集合的区别之List接口
说到Java集合,大家肯定脱口而出List、Set、Map,(想不出来的请自行面壁),今天就详细聊聊大家耳熟能详的List吧。
List接口实现自Collection接口,是Java的集合框架中的一员,List接口下又有ArrayList、LinkedList和线程安全的Vector,今天就简单分析一下ArrayList和LinkedList的异同以及各自的优势。
ArrayList
引用ArrayList集合中的一段注释:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
可见,ArrayList是底层实现是一个可变长度的Object类型的数组,当我们给list插入数据时,就会调用ArrayList的add方法,根据Java提供的看不懂的add方法注解来看,当ArrayList插入数据时,是在ArrayList的尾部进行插入,调用ensureCapacityInternal方法使链表长度+1
ensureCapacityInternal方法会检测自身容量,当链表长度大于ArrayList的默认长度时,ArrayList就会调用grow方法进行扩容
你们就不好奇ArrayList的默认长度是多少吗?emmm,这好像是一个冷知识,好吧,我们就new一个对象插入几个元素试试,下断点瞅瞅吧,下边是我写的测试代码,再下边是见证奇迹的时刻! ArrayList<Integer> list = new ArrayList<Integer>(); for (int i = 1; i <= 2000000; i++) { list.add(i); } list.forEach(System.out::println); //关于lambda表达式,见仁见智吧,我觉得是个好东西 看到刚刚的Math.max没,没看到的翻上去看,显而易见,这个三目运算符,似乎暴露了什么,嗯,ArrayList的默认长度是10。划重点,这个地方可能会考。
继续往下走无聊的断点,那根据Java的意思,就是当list的长度超过10的时候就会调用grow方法进行扩容 如果面试官问你:怎么看待ArrayList和LinkedList各自的特性,怎么看?看源码呗。
以我80多年考专八的丰富经验,grow方法就是要确保ArrayList的容量要足够你的用的,以免送你一个数组越界的异常,回归正题,当面试官问你ArrayList、LinkedList的特性时,你会不假思索的“背”出来:ArrayList擅长查询,其查询的时间是一定的,增删慢,LinkedList与ArrayList相反,如果你知道这些,恭喜你,你大学可以毕业了,因为我毕业的时候就知道这个(我谨代表广大毕业生的最低水平),哪为什么ArrayList在插入元素的时候慢呢,看源码: // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); 你品,你细品,Arrays.copyOf....,嗯,是不是这样就真相了,ArrayList在扩容的时候会把原来的数组复制到另一个内存空间更大的数组中,然后把新元素添加到扩容以后的数组中。所以喽,你不慢谁慢。 哪ArrayList为啥查询快呢?
人家通过下标查找,为啥不快,没有理由啊,你说对吧。不过,在查找的时候小心IndexOutOfBoundsException,他这个异常可不是闹着玩的。
LinkedList
小朋友,你是否有很多问号?为什么,LinkedList插入快?
LinkedList是一个由相互引用的节点组成的双向链表,那么当把数据插入至该链表某个位置时,该数据就会被组装成一个新的节点,随后只需改变链表中对应的两个节点之间的引用关系,使它们指向新节点,即可完成插入。
测试:贴代码
talk is cheap,show me your code.
ArrayList
public class List {
public void array(){
Scanner sc=new Scanner(System.in);
int test = sc.nextInt();
long start = System.currentTimeMillis(); //获取当前时间
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 1; i <= test; i++) {
list.add(i);
}
list.forEach(System.out::println);
long end = System.currentTimeMillis(); // 记录结束时间
System.out.println(end-start);
}
public static void main(String[] args) {
List list = new List();
list.array();
}
}
LinkedList
public class Link {
public void link(){
Scanner sc=new Scanner(System.in);
int test = sc.nextInt();
long start = System.currentTimeMillis(); //获取当前时间
LinkedList<Integer> link = new LinkedList();
for (int i = 1; i <= test; i++) {
link.add(i);
}
link.forEach(System.out::println);
long end = System.currentTimeMillis(); // 记录结束时间
System.out.println(end-start);
}
public static void main(String[] args) {
Link link = new Link();
link.link();
}
}
总结
ArrayList与LinkedList的快慢只是一个相对的,通过看LinkedList源码不难发现,LinkedList在插入数据的时候,会new一个Node节点,新建对象也是一种消耗资源的操作,所以,在数据量较小的时候,如插入500万以上的数据时,ArrayList的速度更快一些,一般情况下我们一般不会同时插入百万级的数据,所以一般都说
ArrayList插入遍历快,增删慢,LinkedList增删快,插入慢。两者皆没有线程安全做处理,而Vector的方法由synchronized关键字修饰,所以Vector是线程安全的List。
完结撒花。
代码我会同步在Github,欢迎访问:
我的Github
我们站在巨人的肩膀上,文章参考了
https://blog.csdn.net/xu962336414/article/details/84032693
- 算法模板——线段树6(二维线段树:区域加法+区域求和)(求助phile)
- 【LeetCode 242】 关关的刷题日记36 Valid Anagram
- javascript闭包
- 【LeetCode 438】关关的刷题日记37 Find All Anagrams in a String
- 还在手动给css加前缀?no!几种自动处理css前缀的方法简介
- 算法模板——线段树5(区间开根+区间求和)
- Spring基础篇——通过Java注解和XML配置装配bean
- Java多线程高并发学习笔记(二)——深入理解ReentrantLock与Condition
- 算法模板——线段树1(区间加法+区间求和)
- 【LeetCode 205】关关的刷题日记38 Isomorphic Strings
- JavaScript基础2---控制权DOM操作
- 算法模板——线段树3(区间覆盖值+区间求和)
- 算法模板——线段树4(区间加+区间乘+区间覆盖值+区间求和)
- 【LeetCode 204】关关的刷题日记39 Count Primes
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 手把手教你使用 Prometheus 监控 JVM
- 基于云开发 CloudBase 搭建在线视频会议应用
- 手搓一个分布式大气监测系统(六)云端能力更新、说明及源码放出
- maybe incorrect parameters such as bit_rate, rate, width or height
- vue 怎么将Checkbox 多选框选中的值提交
- vue-element怎么给select下拉框赋值?
- 小程序生成二维码海报的组件-wxa-plugin-canvas
- kbone 是什么?这可能是最好的小程序开源框架
- jQuery根据填写的input的数值导出excel表格
- 解决多种版本python冲突问题
- 探索 App Clips
- ES索引模糊查询
- Dubbo定时任务时间轮(Time Wheel)算法详解
- Vue 中 data 为什么必须是一个函数
- Windows下制作nodejs后台程序的脚本-开机自启动