看到这个有点懵逼,一时还真不知道怎么解释,能让彻底没有接触过的人都能听懂java
列表,什么是列表呢?数组
比如你到了一个村里,看到每一个房子上的门牌号,如这是张村1号,下一个是张村2号,接着是3号、4号...安全
如今你要去张村10号,那么得,顺着这个门牌一直往下走,就能找到了数据结构
这个村落里的门牌号的形式,就特别像JDK中的 LinkedList
了,找张村10号的过程基本就是 LinkedList#indexOf()
根据索引获取对应值的过程了并发
从这个case里面,小结一下List的特色性能
这个顺序性好说,这个惟一性是我一家之言,多半描述得不太稳当,出发是为了与Map进行对比 ,下面重点说明一下线程
以前的博文Java Map常见问题汇总小结 把Map比做了词典,换在这里比做村牌号,能够这么玩,将户主名和门牌号一一对应,你报一个户主名,我就告诉你他家门牌号,而后就有这么个问题了设计
如今我问张三家门牌是多少?是五号呢仍是105呢?这个就不惟一了code
换成链表的方式,你报一个门牌号,要么这门牌号无效,要么就只有一家在哪儿等着你呢,这就是我所说的惟一性对象
(废话比较多,惋惜没有稿费)
JDK中实现了一些可以覆盖绝大部分场景的List容器,罗列一些常见的以下
根据是否线程安全分类
线程安全 | 非线程安全 |
---|---|
Vector, CopyOnWriteArrayList | ArrayList, LinkedList |
根据获取数据的时间复杂度
O(1) | O(n) |
---|---|
ArrayList, Vector, CopyOnWriteArrayList | LinkedList |
这个分类有点意思了,前面三个,给了索引,立马就把数据给你;这后面的一个,得遍历一把找到对应的位置再返回数据,详情建第四条
底层数据结构
数组 | 链表 |
---|---|
ArrayList, Vector, CopyOnWriteArrayList | LinkedList |
扩容原理分类
动态扩容 | 无扩容一说 |
---|---|
ArrayList, Vector, CopyOnWriteArrayList | LinkedList |
基于数组的链表 ArrayList, 经常使用作有序数据存储的容器,通常使用的三把斧
// 1. 建立数组 List<String> list = new ArrayList<>(); // 2. 添加数据 list.add("word"); list.add(0, "hello"); // 3. 获取数组 list.get(0);
实现原理简要说明
add(obj)
会将数据直接扔进数组中,索引为当前列表的长度size,而后size+1add(index, obj)
, 在索引处添加数据,会致使原数组中,索引以后的数据后移(即会出现数组拷贝)二者的底层存储结构不一样,直接致使最终的使用场景的区别,下面以列表方式给出对比
说明 | ArrayList | LinkedList |
---|---|---|
存储结构 | ![]() |
![]() |
随机访问 | 根据数组索引定位,耗时 O(1) | 从链表头or链表尾遍历,耗时O(n) |
新增数据 | 列表个数超过数组容量,则数组扩容,出现数组拷贝 | 定位到插入位置,新增一个节点插入链表中 |
是否扩容逻辑 | 插入超过上限扩容 <br/> 1. 增长原来空间大小的一半 <br/> 2. 若仍塞不下,则扩充到填满 | 无扩容逻辑 |
应用场景 | 1. 适合随机访问 <br/> 2. 随机插入or删除致使数组拷贝 <br/> 3. 末尾插入,较友好 <br/> | 1. 随机访问性能差,适合遍历场景<br/> 2. 随机插入or删除,先遍历获取位置,而后插入or删除<br/> 3. 链表头尾插入友好 <br/> |
ArrayList 非线程安全,即在遍历一个ArrayList对象时,若出现修改,则会抛一个并发修改异常,一般为了保障线程安全,请使用
CopyOnWriteArrayList
代替,至于Vector,已经退出历史舞台了
Vector
线程安全的列表,其实现是在全部的方法上加了同步锁,确保同一时刻,只有一个线程在访问列表,是一种伪并发的使用方式
CopyOnWriteArrayList
写副本,替换原链表的方式实现线程安全,基本原理以下
把这个单独拎出来有点混字数的感受(多谢也没有啥收益,纯属手欠...)
通常的遍历方式是采用foreach语法
List<String> strList = new ArrayList<>(); // .... for(String str: strList) { System.out.println(str); }
还有一种用得较少,通常是在要遍历的过程当中,修改列表的值时使用
List<String> strList = new ArrayList<>(); Iterator<String> iterator = strList.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); if(xxx) { iterator.remove(); } }
那么问题来了,在第一种方式中,如果修改了会怎么样
@Test public void testRemove() { List<String> strList = new ArrayList<String>(); strList.add("hello"); strList.add("world"); strList.add("test1"); strList.add("test2"); strList.remove(0); int i = 0; for(String str: strList) { System.out.println(str); if(++i == 1) { strList.remove(i); } } }
抛了并发修改异常
彻底照着CopyOnWriteArrayList
抄的话就没意思了,然而让本身去想一个方案,能够怎么搞?实现省略,暂时没想法。。。