JDK List源码分析

List接口定义了有序集合(序列)。在Collection的基础上,增加了可以通过下标索引访问,以及线性查找等功能。

整体类结构

1.AbstractList

  该类作为List的通用骨架实现,和AbstractCollection一样,也是为了减少实现该接口的工作量。为了实现一个只读的List,仅仅只需要实现get和size方法即可。而对于读写的List,则需要额外覆写set,add和remove。

  该类基于get(int),set(int, Object)等方法实现了Iterator方法,基于随机访问的,所以如果如果是链表,还是得重新实现该方法。

  该类有一个重要的属性:protected transient int modCount = 0;,这个属性用于检测多线程并发的,每次对集合的结构发生了变化(如添加或者删除等,由子类实现控制),modCount就会自增。当创建迭代器的时候,迭代器内部就会用一个属性(expectedModCount)来保存当前modCount的值,用于表示当前的状态。在遍历的过程中,如果另一个线程修改了集合(modCount发生变化),在该线程就会检测到expectedModCountmodCount 不一样,从而抛出并发异常(fast-fail)。(并不严格保证并发控制)

私有内部类:Itr和ListItr

  Itr为最基本的迭代器,基于外部类的get等方法来构建。该类有三个属性:cursor用于记录当前的访问位置,expectedModCount保存当前容器状态,lastRet则保存上次访问的结果。而ListItr继承了Itr,添加了双向遍历的功能

  对于迭代器的实现,和Lua等动态语言的闭包类似。

例如next操作:

public E next() {
// 先检查是否被另一个线程修改过
checkForComodification();
try {
int i = cursor;
E next = get(i);
// 保存这次访问的下标
lastRet = i;
// 移动到下一个元素
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
} public void remove() {
// lastRet<0表示已经删除过一次,不允许连续删除(可以控制越界删除)
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); try {
// 调用外部类的删除方法,modCount发生变化
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
// 防止连续删除
lastRet = -1;
// 记录下modCount当前的值
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

2.ArrayList

  List接口的动态数组实现,和Vector最大不同之处在于,它是非同步的。如果需要同步,可以使用Collections.synchronizedList或者加锁同步。

  添加元素的时候,如果当前数组已满,则会先执行扩容,再添加。每次扩展增加为原来容量的1/2,如:newCapacity = oldCapacity + (oldCapacity >> 1),如果新计算出来的容量比所需要的小或者溢出,则只扩展至所需的大小:

private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}

  在删除元素的时候,经过移动后,需要把数组最后一个元素的引用置空,否则会存在强引用,GC无法收集而导致内存泄漏。如:

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; // Let gc do its work
}

3.AbstractSequentialList/LinkedList

  AbstractSequentialList抽象类是为了减少实现序列访问存储的工作量,如链表等,该类通过使用ListIterator迭代器来实现父类的get,set等方法,如果要实现链表,仅仅只需要实现该方法的迭代器即可。

  LinkedList继承了该抽象类,并实现了Deque双端队列的接口,底层采用双向链表的方式实现,其并没有沿用父类的一些实现,如对add的实现,父类通过迭代器从头遍历到尾部,后再添加,这里直接通过尾指针在表尾部添加,以实现O(1)的操作。在使用下标遍历访问时,做了点小优化,如果靠近头部,就从头部开始遍历,靠近尾部,则从尾部开始遍历。如下:

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;
}
}

JDK Collection 源码分析(2)—— List的更多相关文章

  1. JDK Collection 源码分析(1)—— Collection

    JDK Collection   JDK Collection作为一个最顶层的接口(root interface),JDK并不提供该接口的直接实现,而是通过更加具体的子接口(sub interface ...

  2. JDK Collection 源码分析(3)—— Queue

    @(JDK)[Queue] JDK Queue Queue:队列接口,对于数据的存取,提供了两种方式,一种失败会抛出异常,另一种则返回null或者false.   抛出异常的接口:add,remove ...

  3. JDK AtomicInteger 源码分析

    @(JDK)[AtomicInteger] JDK AtomicInteger 源码分析 Unsafe 实例化 Unsafe在创建实例的时候,不能仅仅通过new Unsafe()或者Unsafe.ge ...

  4. Java集合框架之接口Collection源码分析

    本文我们主要学习Java集合框架的根接口Collection,通过本文我们可以进一步了解Collection的属性及提供的方法.在介绍Collection接口之前我们不得不先学习一下Iterable, ...

  5. 设计模式(十八)——观察者模式(JDK Observable源码分析)

    1 天气预报项目需求,具体要求如下: 1) 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方). 2) 需要设计开放型 API,便于其他第三方也能接入气象 ...

  6. java.util.Collection源码分析和深度讲解

    写在开头 java.util.Collection 作为Java开发最常用的接口之一,我们经常使用,今天我带大家一起研究一下Collection接口,希望对大家以后的编程以及系统设计能有所帮助,本文所 ...

  7. 集合源码分析[1]-Collection 源码分析

    目录 Collection 1. 介绍 2. 继承关系 3. 方法 4. JDK8新增的方法 removeIf(Predicate<? super E> filter) Spliterat ...

  8. 集合源码分析[3]-ArrayList 源码分析

    历史文章: Collection 源码分析 AbstractList 源码分析 介绍 ArrayList是一个数组队列,相当于动态数组,与Java的数组对比,他的容量可以动态改变. 继承关系 Arra ...

  9. Java中集合框架,Collection接口、Set接口、List接口、Map接口,已经常用的它们的实现类,简单的JDK源码分析底层实现

    (一)集合框架: Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(实现接口的类).所有抽象出来的数据结构和操作(算法)统称为集合框架. 程序员在具体应用的时候,不必考虑数据结构和 ...

随机推荐

  1. Mysql有用的面试题

    A.一道SQL语句面试题,关于group by表内容:2005-05-09 胜2005-05-09 胜2005-05-09 负2005-05-09 负2005-05-10 胜2005-05-10 负2 ...

  2. Linux IPC socket 广播,组播

    getsockopt()/setsockopt() //获得sockfd指向的socket的属性 int getsockopt(int sockfd, int level, int optname, ...

  3. redis 集群创建常见几个问题

    Redis配置集群遇到问题及解决方法   配置完所有主节点后,报" ERR Invalid node address specified" 由于Redis-trib.rb 对域名或 ...

  4. PHP函数整理(二)

    以下均参考自 php.net 1.curl_setopt_array() 此函数为CURL传输会话批量设置选项.这个函数对于需要设置大量的curl选项是非常有用的,不需要重复的调用curl_setop ...

  5. Linux – Usermod命令参数解析和实例说明

    usermod 命令修改系统帐户文件来反映通过命令行指定的变化 1. 首先看看usermod都是有哪些参数 [root@hxweb101 ~]$ usermod --help Usage: userm ...

  6. 【转】App架构设计经验谈:接口的设计

    App架构设计经验谈:接口的设计 App与服务器的通信接口如何设计得好,需要考虑的地方挺多的,在此根据我的一些经验做一些总结分享,旨在抛砖引玉. 安全机制的设计 现在,大部分App的接口都采用REST ...

  7. C#学习笔记-图像处理篇(一)绘制公章

    上个月免费帮别人做事,就一直在服务员和程序员中来回切换,所以好多事情都弄得乱糟糟的,家里也是乱七八糟的,所以学习这一块也放了一段时间. 刚刚开始当服务员的时间看到了形形色色的人,觉得很好玩,他们每个人 ...

  8. ACM失败之路

    校赛打完,已过四月,该是准备背起行囊,踏上考研之路了,自然,得先阔别一下ACM了,想起这几年ACM路,感慨颇多,不得不一诉心肠,与大家分享一下我的ACM历程,如果有人能从此文获取一些益处,那我就很欣慰 ...

  9. 骨骼蒙皮动画算法(Linear Blending Skinning)

    交互式变形是编辑几何模型的重要手段,目前出现了许多实时.直观的交互式变形方法.本文介绍一种利用线性混合蒙皮(Linear Blending Skinning,LBS)技术来实现网格变形的方法,线性混合 ...

  10. 洛谷P1341 无序字母对[无向图欧拉路]

    题目描述 给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒).请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现. 输入输出格式 输入格式: 第一行输入一 ...