List 接口以及实现类和相关类源码分析


List接口分析

接口描述

用户可以对列表进行随机的读取(get),插入(add),删除(remove),修改(set),也可批量增加(addAll),删除(removeAll,retainAll),获取(subList)。
还有一些判定操作:包含(contains[All]),相等(equals),索引(indexOf,lastIndexOf),大小(size)。
还有获取元素类型数组的操作:toArray()

注意事项

两种迭代器IteratorListIterator:
Iterator 正向遍历列表元素的迭代器
ListIterator 支持正向和反向列表元素的迭代器,也支持插入和删除元素,也能从列表中指定位置开始的列表迭代器
插入和删除元素
List接口的不同实现,在插入和删除上开销不一样,在使用具体实现的时候注意区分。
元素限制
某些实现可能包含的元素有限制,某些实现禁止null元素,某些查询不合格的元素是否存在可能会抛出异常,而某些可能返回false

ArrayList源码分析

类描述

随机访问列表,可clone,可序列化,允许元素为null在内的所有元素,非同步类(非线程安全)。
访问元素是O(1)时间,添加元素是O(n)时间。 size <= capacity。

注意事项

  • 此实现是非同步的
    如果多线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改 了列表,那么它必须保持同步。一般是使用自然封装该列表的对象进行同步操作来完成。或者使用Collections.synchronizedList()方法来获取该实现的同步视图,最好在创建时完成,以防意外对列表进行不同步的访问。
  • 迭代器
    该实现返回的iteratorlistIterator快速失败的:再创建迭代器之后,除非通过迭代器自身的removeadd方法从结构上对列表进行修改,否则在任何时间以任何形式对列表进行修改,迭代器都会抛出ConcurrentModificationException。因此面对并发的修改,迭代器很快就会完全失败。

结构上的修改:指的是添加或删除一个或多个元素;显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。
快速失败无法得到保证,一般来说,该行为仅用于检测bug。快速失败迭代器会 尽最大可能抛出ConcurrentModificationException,因此,为提高此类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法。

  • 从源码看迭代器快速失败行为
    ArrayList类中有modCount属性来记录对象修改的次数,迭代器内部类Itr中有expectedModCount属性,该属性初始化为外部类的modCount,客户端调用ArrayList对象的iterator()方法时,ArrayList新建一个Itr实例返回。
    每调用一次外部类中的所有修改内部数组结构的方法,modCount属性加1,这样在多线程环境下,一个线程修改了内部数组的结构,另一个线程使用迭代器遍历数组,将会产生expectedModCountmodCount不一致的情况,因此会抛出ConcurrentModificationException异常。
    而通过迭代器 修改内部数组结构,则不会抛出异常,为什么呢?迭代器也是通过调用外部类的方法来移除数组中的某个元素,在移除元素后,迭代器在方法中将expectedModCount更新为modCount
  • 一些源码中学到的其他东西
    1. 每次扩充容量都是扩充原来容量的1.5倍。(源码对边界越界情况检查的非常严格,值得学习)
    2. 删除元素(批量删除,个体删除,全部删除),删除完之后将引用设置为null,方便GC回收垃圾。
    3. 序列化和反序列化,先将size序列化,再逐个序列化元素。反序列化也一样,先反序列化size,再逐个反序列化元素。

LinkedList源码分析

类描述

接口ListDeque的实现类,可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的同步视图、不可修改视图和检查类型视图通过源码进行分析

  • SynchronizedListSynchronizedRandomAccessList
    着重分析SynchronizedList,后者是继承了前者,在subList操作上不一样(前者的subList操作返回的子列表需要包装为SynchronizedList而后者需要包装为SynchronizedRandomAccessList)。
    SynchronizedList类其实是List实现类的同步视图,将实现类组合到自身,并且自身包含一个对象锁,每次调用List接口的某个操作,都会锁定整个方法,然后将请求委托给实现类,以此达到同步的目的。

题外话:同步视图是对整个方法进行加锁,串化执行,这样的效率显然不是满足高并发的需求,多线程同时竞争一个锁,这和单线程有什么区别。

  • UnmodifiableListUnmodifiableRandomAccessList
    此两个内部类是List实现类的不能修改类视图(无论是修改结构还是元素值),调用修改对象的方法将抛出UnsupportedOperationException异常。
  • CheckedListCheckedRandomAccessList
    这两个内部类是List实现类的检查类型视图,构造该内部类的时候,需要传递给构造方法元素类型信息,每个读取方法都返回和类型匹配的元素,每个设置方法都先检查类型是否匹配。

List 接口以及实现类和相关类源码分析的更多相关文章

  1. 7.Java集合-Arrays类实现原理及源码分析

    Java集合---Arrays类源码解析  转自:http://www.cnblogs.com/ITtangtang/p/3948765.html 一.Arrays.sort()数组排序 Java A ...

  2. Java基础系列--07_Object类的学习及源码分析

    Object: 超类 (1)Object是类层次结构的顶层类,是所有类的根类,超类.   所有的类都直接或者间接的继承自Object类.   所有对象(包括数组)都实现这个类的方法 (2)Object ...

  3. Orchard源码分析(5):Host相关(Orchard.Environment.DefaultOrchardHost类)

    概述 Host 是应用程序域级的单例,代表了Orchard应用程序.其处理应用程序生命周期中的初始化.BeginRequest事件.EndRequest事件等. 可以简单理解为HttpApplicat ...

  4. java中List接口的实现类 ArrayList,LinkedList,Vector 的区别 list实现类源码分析

    java面试中经常被问到list常用的类以及内部实现机制,平时开发也经常用到list集合类,因此做一个源码级别的分析和比较之间的差异. 首先看一下List接口的的继承关系: list接口继承Colle ...

  5. Struts2 源码分析——Result类实例

    本章简言 上一章笔者讲到关于DefaultActionInvocation类执行action的相关知识.我们清楚的知道在执行action类实例之后会相关处理返回的结果.而这章笔者将对处理结果相关的内容 ...

  6. Struts2 源码分析——Action代理类的工作

    章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息.相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识.而本章将讲到关于struts2启动成功 ...

  7. Spring源码分析——BeanFactory体系之抽象类、类分析(二)

    上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...

  8. Spring源码分析——资源访问利器Resource之实现类分析

    今天来分析Spring的资源接口Resource的各个实现类.关于它的接口和抽象类,参见上一篇博文——Spring源码分析——资源访问利器Resource之接口和抽象类分析 一.文件系统资源 File ...

  9. Spring Boot 2.x 启动全过程源码分析(上)入口类剖析

    Spring Boot 的应用教程我们已经分享过很多了,今天来通过源码来分析下它的启动过程,探究下 Spring Boot 为什么这么简便的奥秘. 本篇基于 Spring Boot 2.0.3 版本进 ...

随机推荐

  1. 无法为请求的 Configuration 对象创建配置文件 错误原因

    Configuration config = WebConfigurationManager.OpenWebConfiguration("~"); 无法为请求的 Configura ...

  2. Samza文档翻译 : Comparison Introduction

    http://samza.incubator.apache.org/learn/documentation/0.7.0/comparisons/introduction.html 这里有一些使得Sam ...

  3. RASP 完爆 WAF 的5大理由!

    Web 应用防火墙(WAF)已经成为常见 Web 应用普遍采用的安全防护工具,即便如此,WAF 提供的保护方案仍旧存在诸多不足,笔者认为称 WAF 为好的安全监控工具更为恰当.幸运的是,应用安全保护技 ...

  4. 查看使用的Eclipse版本

    第一种方法 1. 找到Eclipse的解压目录就是你的Eclipse.exe 所在的目录 2. 找到 .eclipseproduct 文件双击打开

  5. MyBatis的动态SQL操作--查询

    查询条件不确定,需要根据情况产生SQL语法,这种情况叫动态SQL,即根据不同的情况生成不同的sql语句. 模拟一个场景,在做多条件搜索的时候,

  6. C# 自定义光标 WaitCursor

    一种: 把图像文件放到项目的文件夹中 1 如果图像文件是.cur格式: Cursor cur=new Cursor(文件名); this.cursor=cur; 两句话 就完事 2 如果图像文件是其他 ...

  7. 254 shades of grey

    254 shades of grey Description: Why would we want to stop to only 50 shades of grey? Let's see to ho ...

  8. Android开发之xUtils-HttpUtils的使用

    使用xUtils框架中的网络部分HttpUtils,功能:下载,断点续传,progressbar显示进度,文本显示进度% import java.io.File; import android.app ...

  9. ADT中的代码补全设置

    设置自动补全代码 刚刚学Android,有很多变量和方法 都不熟悉.需要有提示,才更加方便. 快捷方式:Alt + /    可以出现代码提示. 默认的只有输入“ .” 以后才会有代码补全提示,可作如 ...

  10. 解决windows下vim方向键变成 ABCD 的问题

    一.问题描述: windows下面要安装git --> 安装了 msys2 -->安装并更新了 vim(7.4),然后在使用过程中发现vim不能使用  BACKSPACE 键,按方向键会打 ...