本篇随笔主要描述的是我阅读 ArrayList 源码期间的对于 ArrayList 的一些实现上的个人理解,有不对的地方,请指出~

  先来看一下 ArrayList 的继承图:

  

  由图可以看出,ArrayList 的父类有 AbstractList、 AbstractCollection ,所以我从 AbstractCollection 类开始阅读。

  

  一、AbstractCollection 类相关。

    AbstractCollection 类实现了 Collection 接口,并且由于 AbstractCollection 是一个抽象类,所以它只实现了一些 Collection 接口中的方法,例如 toArray() 方法, contains() 方法, remove() 方法等等。对于Collection 接口中含有的其它方法仍令其保持抽象实现,我认为这样实现的原因是 :抽象类的作用就是一个概括作用,它需要将其子类中含有的公共方法在抽象类中加以实现,而抽象类中仍然保持抽象的方法一般都是每个子类有自己的实现,在抽象类中是没有办法统一起来的。

    在 AbstractCollection 类中,我以为 finishToArray() 方法值得注意一下:

    

  

    finishToArray() 方法是用来给数组 r[] 扩容的,每次当数组 r 的容量不足以容纳迭代器遍历到的元素时,就会对数组 r 进行扩容,并且对扩容后的数组容量进行校验(hugeCapacity())。

     扩容方法:newCap = cap + (cap >> 1)+ 1;

  二、AbstractList 相关

    AbstractList 也是一个抽象类,其下面具体的子类主要有 LinkedList, ArrayList, Vector几种。所以 AbstractList 中含有的方法主要是对这几个具体的子类的抽象。

    我认为 AbstractList 中主要有以下几点值得注意:

    1、AbstractList 中 iterator() 和 listIterator() 均采用内部类方式实现。

    

 public Iterator<E> iterator() {
return new Itr();
} public ListIterator<E> listIterator() {
return listIterator(0);
} public ListIterator<E> listIterator(final int index) {
rangeCheckForAdd(index); return new ListItr(index);
} private class Itr implements Iterator<E> { ...... } private class ListItr extends Itr implements ListIterator<E> {
......
}

    2、AbstractList 中 removeRange() 方法为我们展示了如何运用迭代器移除 list 指定范围的元素。(相比于 ArrayList 中的 removeRange 方法,用迭代器实现的 removeRange 复杂度                   较高)

  

  protected void removeRange(int fromIndex, int toIndex) {
ListIterator<E> it = listIterator(fromIndex);
for (int i=0, n=toIndex-fromIndex; i<n; i++) {
it.next();
it.remove();
}
}

    3、并发修改异常 ConcurrentModificationException

      在 AbstractList 中通过 expectedModCount 和 modCount 两个量比较来判断是否产生了并发修改异常,当迭代器在迭代过程中发现 expectedModCount 和 modCount 两个量不相                 等时就会抛出并发修改异常。modCount 代表改动 list 的次数,当获得一个 iterator 或者 listIterator 时,就会将 modCount 值赋给 expectedModCount,在迭代器使用的过程中,如果出                 现两个值不相等的情况 ,就证明有迭代器之外的操作改动了 list,而这很可能会导致迭代器对 list 的操作出现错误,所以在接下来使用迭代器的时候就会抛出异常,这就是并发修改异                 常的作用。

    4、AbstractList 中的 subList() 方法。

      先来看下 subList() 方法的实现:

      

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
......
public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<>(this, fromIndex, toIndex) :
new SubList<>(this, fromIndex, toIndex));
}
......
}
class SubList<E> extends AbstractList<E> {
SubList(AbstractList<E> list, int fromIndex, int toIndex) {
l = list;
offset = fromIndex;
size = toIndex - fromIndex;
this.modCount = l.modCount;
}
public E set(int index, E element) {
return l.set(index+offset, element);
}
......
}
class RandomAccessSubList<E> extends SubList<E> implements RandomAccess {
RandomAccessSubList(AbstractList<E> list, int fromIndex, int toIndex) {
super(list, fromIndex, toIndex);
}
......
}

      可见有 SubList 与 RandomAccessSubList 两种,区别在于是否实现 RandomAccess 接口 ,也就是是否可以随机读取,例如 ArrayList 可以随机读取,而 LinkedList 则只能顺序读                 取。在SubList类的构造方法中,获取了一个 AbstractList 类的引用,作用是利用这个引用实现 SubList 中的相关方法。

      也就是说,subList 几乎所有方法都是基于 AbstractList 类实现的,对于 subList  返回的 list 所做的所有改动都会反应到原来的 list 当中去。

  三、ArrayList 相关

          我认为 ArrayList 源码中以下几点值得注意:

      1、在 ArrayList 把集合当做构造器中参数涉及到的 JDK BUG,源码如下:

      

 public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}

        在源码中可以看到由于  c.toArray might (incorrectly) not return Object[] ,所以运用反射进行了 class 类型的判断,那么什么时候会出现这种情况呢?下面举例说明:

      

 public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("hello"); list1.add("world");
Object[] o1 = list1.toArray();
System.out.println(o1.getClass());
//下面这句代码可以正常执行
o1[0] = new Integer(1); List list = Arrays.asList("hello", "world", "i", "love", "you");
Object[] o = list.toArray();
System.out.println(o.getClass());
//下面这句代码会抛出java.lang.ArrayStoreException异常
o[0] = new Integer(1);
}

      输出结果为:

      

      可以看到 list 与 list1 的 toArray 方法返回的数组类型是不相同的,如果尝试向 String 类型数组中插入其它类型元素,就会抛出异常。所以

      public ArrayList(Collection<? extends E> c) {} 方法中要对 collection.toArray() 方法返回的数组类型进行判断,如果不是 Object[],就要新建一个 Object[],并进行复制。

    2、ArrayList 扩容方法

      

 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);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}

      采用 oldCapacity + (oldCapacity >> 1)来扩充容量。

    3、与 AbstractList 相似,ArrayList 的 subList 方法也是基于 ArrayList 实现的,对于 subList 产生的 list 的所有操作都会反映到原来的 ArrayList 上。

  ArrayList 源码相关就介绍到这里。

      

JDK1.8源码阅读系列之一:ArrayList的更多相关文章

  1. JDK1.8源码阅读系列之三:Vector

    本篇随笔主要描述的是我阅读 Vector 源码期间的对于 Vector 的一些实现上的个人理解,用于个人备忘,有不对的地方,请指出- 先来看一下 Vector 的继承图: 可以看出,Vector 的直 ...

  2. JDK1.8源码阅读系列之四:HashMap (原创)

    本篇随笔主要描述的是我阅读 HashMap 源码期间的对于 HashMap 的一些实现上的个人理解,用于个人备忘,有不对的地方,请指出- 接下来会从以下几个方面介绍 HashMap 源码相关知识: 1 ...

  3. JDK1.8源码阅读系列之二:LinkedList

    本篇随笔主要描述的是我阅读 LinkedList 源码期间的对于 LinkedList 的一些实现上的个人理解,有不对的地方,请指出- 先来看一下 LinkedList 的继承图: 由于 Abstra ...

  4. 源码阅读系列:EventBus

    title: 源码阅读系列:EventBus date: 2016-12-22 16:16:47 tags: 源码阅读 --- EventBus 是人们在日常开发中经常会用到的开源库,即使是不直接用的 ...

  5. 【Dubbo源码阅读系列】之远程服务调用(上)

    今天打算来讲一讲 Dubbo 服务远程调用.笔者在开始看 Dubbo 远程服务相关源码的时候,看的有点迷糊.后来慢慢明白 Dubbo 远程服务的调用的本质就是动态代理模式的一种实现.本地消费者无须知道 ...

  6. 【Dubbo源码阅读系列】服务暴露之本地暴露

    在上一篇文章中我们介绍 Dubbo 自定义标签解析相关内容,其中我们自定义的 XML 标签 <dubbo:service /> 会被解析为 ServiceBean 对象(传送门:Dubbo ...

  7. Spring源码阅读系列总结

    最近一段时间,粗略的查看了一下Spring源码,对Spring的两大核心和Spring的组件有了更深入的了解.同时在学习Spring源码时,得了解一些设计模式,不然阅读源码还是有一定难度的,所以一些重 ...

  8. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  9. 【合集】TiDB 源码阅读系列文章

    [合集]TiDB 源码阅读系列文章 (一)序 (二)初识 TiDB 源码 (三)SQL 的一生 (四)INSERT 语句概览 (五)TiDB SQL Parser 的实现 (六)Select 语句概览 ...

随机推荐

  1. Visual Studio 如何给生成的exe加入多个图标资源

    Visual Studio 如何给生成的exe加入多个图标资源(快捷方式可调用) 方法: 打开你的VS, 文件—>新建—>文件 常规—>选择本机资源模板(rct文件) 如图一 新建后 ...

  2. [ext/iconv/iconv.lo] Error 1

    办法1: wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.13.1.tar.gz tar -zxvf libiconv-1.13.1.tar.g ...

  3. WinForm窗体淡入效果界面的简单实现方法

    WinForm窗体淡入效果主要使用到控件的Opacity属性 首先在WinForm窗体中拖入一个Timer控件,然后再Timer控件的Tick事件添加如下代码: private void timer1 ...

  4. Mac OS X下环境搭建 Sublime Text 2 环境变量配置 开发工具配置Golang (Go语言)

    Golang (Go语言) Mac OS X下环境搭建 环境变量配置 开发工具配置 Sublime Text 2 一.安装Golang的SDK 在官网http://golang.org/ 直接下载安装 ...

  5. Memcached安装配置最大使用内存

    Memcached安装配置最大使用内存 项目做了很多,虽然用memcached的项目也有很多.但是没有太关注安装memcached细节问题 最近做了一个项目,把很多东西都放到memcached缓存中, ...

  6. url路径

      /:表示根目录(最原始开头的那个目录): ~/:表示虚拟站点根目录: ../:表示上级目录(当前目录的上一级),相当于在当前目录按了一次后退键: ../../:表示上上级目录: ./:当前目录(当 ...

  7. What skills are needed for machine learning jobs

    What skills are needed for machine learning jobs?机器学习工作必须技能 原文: http://www.quora.com/Machine-Learnin ...

  8. [转]Installing Snow Leopard (Client) on VMware Fusion 6.0.3

    Source: http://inficererk.wordpress.com/2014/05/29/installing-snow-leopard-client-on-vmware-fusion-6 ...

  9. [转]Creating an iPhone Daemon

    ref: http://chrisalvares.com/blog/7/creating-an-iphone-daemon-part-1/ http://chrisalvares.com/blog/3 ...

  10. mac osx 10.9安装配置macvim

    如果你已经安装了macvim,升级后又不能用了,建议你可以看看http://kodira.de/2013/10/macvim-osx-10-9-mavericks/这篇文章,如果你还没有安装,下面由我 ...