List 接口以及实现类和相关类源码分析
List 接口以及实现类和相关类源码分析
List接口分析
接口描述
用户可以对列表进行随机的读取(get),插入(add),删除(remove),修改(set),也可批量增加(addAll),删除(removeAll,retainAll),获取(subList)。
还有一些判定操作:包含(contains[All]),相等(equals),索引(indexOf,lastIndexOf),大小(size)。
还有获取元素类型数组的操作:toArray()
注意事项
两种迭代器Iterator和ListIterator:
Iterator 正向遍历列表元素的迭代器
ListIterator 支持正向和反向列表元素的迭代器,也支持插入和删除元素,也能从列表中指定位置开始的列表迭代器
插入和删除元素:
List接口的不同实现,在插入和删除上开销不一样,在使用具体实现的时候注意区分。
元素限制:
某些实现可能包含的元素有限制,某些实现禁止null元素,某些查询不合格的元素是否存在可能会抛出异常,而某些可能返回false
ArrayList源码分析
类描述
随机访问列表,可clone,可序列化,允许元素为null在内的所有元素,非同步类(非线程安全)。
访问元素是O(1)时间,添加元素是O(n)时间。 size <= capacity。
注意事项
- 此实现是非同步的
如果多线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改 了列表,那么它必须保持同步。一般是使用自然封装该列表的对象进行同步操作来完成。或者使用Collections.synchronizedList()方法来获取该实现的同步视图,最好在创建时完成,以防意外对列表进行不同步的访问。 - 迭代器
该实现返回的iterator和listIterator是 快速失败的:再创建迭代器之后,除非通过迭代器自身的remove和add方法从结构上对列表进行修改,否则在任何时间以任何形式对列表进行修改,迭代器都会抛出ConcurrentModificationException。因此面对并发的修改,迭代器很快就会完全失败。
结构上的修改:指的是添加或删除一个或多个元素;显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。
快速失败无法得到保证,一般来说,该行为仅用于检测bug。快速失败迭代器会 尽最大可能抛出ConcurrentModificationException,因此,为提高此类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法。
- 从源码看迭代器快速失败行为
ArrayList类中有modCount属性来记录对象修改的次数,迭代器内部类Itr中有expectedModCount属性,该属性初始化为外部类的modCount,客户端调用ArrayList对象的iterator()方法时,ArrayList新建一个Itr实例返回。
每调用一次外部类中的所有修改内部数组结构的方法,modCount属性加1,这样在多线程环境下,一个线程修改了内部数组的结构,另一个线程使用迭代器遍历数组,将会产生expectedModCount和modCount不一致的情况,因此会抛出ConcurrentModificationException异常。
而通过迭代器 修改内部数组结构,则不会抛出异常,为什么呢?迭代器也是通过调用外部类的方法来移除数组中的某个元素,在移除元素后,迭代器在方法中将expectedModCount更新为modCount。 - 一些源码中学到的其他东西
- 每次扩充容量都是扩充原来容量的1.5倍。(源码对边界越界情况检查的非常严格,值得学习)
- 删除元素(批量删除,个体删除,全部删除),删除完之后将引用设置为
null,方便GC回收垃圾。 - 序列化和反序列化,先将
size序列化,再逐个序列化元素。反序列化也一样,先反序列化size,再逐个反序列化元素。
LinkedList源码分析
类描述
接口List和Deque的实现类,可clone,可序列化,允许null元素,除了List接口之外,该实现类还在列表的开头和结尾获取,删除,插入元素提供了统一的命名方法,允许这些操作将列表作为堆栈、队列 和 双端队列使用。
在结尾插入元素的操作:
add(e)、addAll(Collection)、addLast(E)、boolean offer(E)、boolean offerLast(E)。
其他操作参见JDK文档。一定不要想当然的认为push或pop等操作就是在结尾操作元素的,在使用过程中一定要仔细查阅API
该实现类也不是同步的,多线程修改也会造成对象状态不一致的情景,同样可以使用Collections.synchronizedList()方法得到同步视图。
迭代器也是快速失败的,对结构上的修改,除非通过迭代器自身的修改,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException异常。
注意事项
和ArrayList的注意事项类似,可以参考ArrayList的注意事项。
为什么LinkedList类实现了Deque接口,而ArrayList类却没有呢?
LinkedList类内部实现是链表形式,对链表的插入、删除时间复杂度都是O(1),并且,队列、双端队列和堆栈的操作大部分都是在链表的头部和结尾,插入删除非常方便。 而ArrayList类的内部实现是数组,数组是随机访问速度比较快,但是在头部的插入和删除需要挪动整个数组(或者得时刻记录这头部下标,这样造成的复杂程度和联动反应“比较大”),代价略大。
List同步类(同步视图)
在上面两个实现类中我们可以看到,它们都是非同步的,所以在多线程环境下是不能使用的(只读必须可以啊),特别是多线程中有修改列表结构的线程,那么出错的概率将会很大。
于是Collections类提供了synchronizedList()静态方法来讲非同步的List实现类包装成同步类(我更习惯称之为同步视图)。
说到这里不得不提一个类Collections。
Collections类描述
该类完全由在Collection上进行操作或返回Collection的静态方法组成。它包含在Collection上操作的多态算法(排序、查找等)
如果为此类的方法提供的Collection或类对象为null的话,这些方法将抛出NullPointerException。
该类内部提供了几种包装器类,例如:不可修改的xxx,同步的xxx,检查类型的xxx(其中xxx为Collection下的接口或实现类)。
针对List的同步视图、不可修改视图和检查类型视图通过源码进行分析
SynchronizedList和SynchronizedRandomAccessList
着重分析SynchronizedList,后者是继承了前者,在subList操作上不一样(前者的subList操作返回的子列表需要包装为SynchronizedList而后者需要包装为SynchronizedRandomAccessList)。
SynchronizedList类其实是List实现类的同步视图,将实现类组合到自身,并且自身包含一个对象锁,每次调用List接口的某个操作,都会锁定整个方法,然后将请求委托给实现类,以此达到同步的目的。
题外话:同步视图是对整个方法进行加锁,串化执行,这样的效率显然不是满足高并发的需求,多线程同时竞争一个锁,这和单线程有什么区别。
UnmodifiableList和UnmodifiableRandomAccessList
此两个内部类是List实现类的不能修改类视图(无论是修改结构还是元素值),调用修改对象的方法将抛出UnsupportedOperationException异常。CheckedList和CheckedRandomAccessList
这两个内部类是List实现类的检查类型视图,构造该内部类的时候,需要传递给构造方法元素类型信息,每个读取方法都返回和类型匹配的元素,每个设置方法都先检查类型是否匹配。
完
List 接口以及实现类和相关类源码分析的更多相关文章
- 7.Java集合-Arrays类实现原理及源码分析
Java集合---Arrays类源码解析 转自:http://www.cnblogs.com/ITtangtang/p/3948765.html 一.Arrays.sort()数组排序 Java A ...
- Java基础系列--07_Object类的学习及源码分析
Object: 超类 (1)Object是类层次结构的顶层类,是所有类的根类,超类. 所有的类都直接或者间接的继承自Object类. 所有对象(包括数组)都实现这个类的方法 (2)Object ...
- Orchard源码分析(5):Host相关(Orchard.Environment.DefaultOrchardHost类)
概述 Host 是应用程序域级的单例,代表了Orchard应用程序.其处理应用程序生命周期中的初始化.BeginRequest事件.EndRequest事件等. 可以简单理解为HttpApplicat ...
- java中List接口的实现类 ArrayList,LinkedList,Vector 的区别 list实现类源码分析
java面试中经常被问到list常用的类以及内部实现机制,平时开发也经常用到list集合类,因此做一个源码级别的分析和比较之间的差异. 首先看一下List接口的的继承关系: list接口继承Colle ...
- Struts2 源码分析——Result类实例
本章简言 上一章笔者讲到关于DefaultActionInvocation类执行action的相关知识.我们清楚的知道在执行action类实例之后会相关处理返回的结果.而这章笔者将对处理结果相关的内容 ...
- Struts2 源码分析——Action代理类的工作
章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息.相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识.而本章将讲到关于struts2启动成功 ...
- Spring源码分析——BeanFactory体系之抽象类、类分析(二)
上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...
- Spring源码分析——资源访问利器Resource之实现类分析
今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...
- Spring Boot 2.x 启动全过程源码分析(上)入口类剖析
Spring Boot 的应用教程我们已经分享过很多了,今天来通过源码来分析下它的启动过程,探究下 Spring Boot 为什么这么简便的奥秘. 本篇基于 Spring Boot 2.0.3 版本进 ...
随机推荐
- 4.1 spring-alias 标签的解析;
对于之前漫长的,最核心的Bean标签的解析就没什么好讲的了, 首先看看使用方法: <bean id="car" name="cat0" class=&qu ...
- 【高斯消元】BZOJ 1770: [Usaco2009 Nov]lights 燈
Description 貝希和她的閨密們在她們的牛棚中玩遊戲.但是天不從人願,突然,牛棚的電源跳閘了,所有的燈都被關閉了.貝希是一個很膽小的女生,在伸手不見拇指的無盡的黑暗中,她感到驚恐,痛苦與絕望. ...
- "Principles of Reactive Programming" 之 <Persistent Actor State>学习笔记
这是<Pinciples of Reactive Programming>week6的最后一课. 为什么需要把actor的状态持久化? 如果actor没有状态,那么在任何实时,这个acto ...
- linux为命令取别名
在linux的命令中,有些命令很长并且经常使用到,我们可以为命令添加一个别名,格式如下: $ alias 别名='命令' 例如: # 列出home文件夹的文件 $ alias lsh='ls -l / ...
- 【NOIP 2016 总结】
距离杯赛已经很久了,然而我现在才打总结.. 我好惨的说..两场才380... DAY 1 第一题 toy 送分题,模拟的时候+一下再mod一下就好. [当时打完这题就没再看一眼了,好方的说] #inc ...
- [itint5]下一个排列
http://www.itint5.com/oj/#6 首先,试验的时候要拿5个来试,3,4个都太少了.好久没做所以方法也忘了,是先从后往前找到第一个不合顺序的,然后在后面找到比这个大的最小的来交换, ...
- 非常好的Demo网站
http://www.xdemo.org/
- MyEclipse下查看Java API帮助文档
每次重装JDK或者升级JDK时,都会忘了如何使MyEclipse关联帮助文档.然后,再花十几分钟重新google搜索,麻烦! 首先下载Javadoc api帮助文档,google搜一下就行了. MyE ...
- node.js模块值formidable
模块地址:https://github.com/felixge/node-formidable var formidable = require('formidable'), http = requi ...
- node.js模块之util模块
util提供了各种使用的工具.require('util') to access them. Util.format(format,[..]) Returns a formatted string u ...