博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JDK1.8源码阅读系列之一:ArrayList
阅读量:6787 次
发布时间:2019-06-26

本文共 5585 字,大约阅读时间需要 18 分钟。

  本篇随笔主要描述的是我阅读 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() 均采用内部类方式实现。

    

1 public Iterator
iterator() { 2 return new Itr(); 3 } 4 5 public ListIterator
listIterator() { 6 return listIterator(0); 7 } 8 9 public ListIterator
listIterator(final int index) {10 rangeCheckForAdd(index);11 12 return new ListItr(index);13 }14 15 private class Itr implements Iterator
{16 17 ......18 19 }20 21 private class ListItr extends Itr implements ListIterator
{22 ......23 }

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

  

1  protected void removeRange(int fromIndex, int toIndex) {2         ListIterator
it = listIterator(fromIndex);3 for (int i=0, n=toIndex-fromIndex; i

    3、并发修改异常 ConcurrentModificationException

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

 

    4、AbstractList 中的 subList() 方法。

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

      

public abstract class AbstractList
extends AbstractCollection
implements List
{ ...... public List
subList(int fromIndex, int toIndex) { return (this instanceof RandomAccess ? new RandomAccessSubList<>(this, fromIndex, toIndex) : new SubList<>(this, fromIndex, toIndex)); } ......}class SubList
extends AbstractList
{ SubList(AbstractList
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
extends SubList
implements RandomAccess { RandomAccessSubList(AbstractList
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,源码如下:

      

1 public ArrayList(Collection
c) {2 elementData = c.toArray();3 size = elementData.length;4 // c.toArray might (incorrectly) not return Object[] (see 6260652)5 if (elementData.getClass() != Object[].class)6 elementData = Arrays.copyOf(elementData, size, Object[].class);7 }

 

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

      

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

      输出结果为:

      

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

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

    2、ArrayList 扩容方法

      

1 private void grow(int minCapacity) { 2         // overflow-conscious code 3         int oldCapacity = elementData.length; 4         int newCapacity = oldCapacity + (oldCapacity >> 1); 5         if (newCapacity - minCapacity < 0) 6             newCapacity = minCapacity; 7         if (newCapacity - MAX_ARRAY_SIZE > 0) 8             newCapacity = hugeCapacity(minCapacity); 9         // minCapacity is usually close to size, so this is a win:10         elementData = Arrays.copyOf(elementData, newCapacity);11 }

 

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

 

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

 

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

 

      

 

转载于:https://www.cnblogs.com/Michaelwjw/p/6290847.html

你可能感兴趣的文章
算法导论——基数排序(基于计数排序)
查看>>
19.TCP的交互数据流
查看>>
字符串匹配的Boyer-Moore算法
查看>>
memcached数据库未授权访问漏洞解决
查看>>
centos 7 安装在vmware Workstation的网卡问题 RHEL7
查看>>
嵌入式开发平台-iTOP-4418开发板
查看>>
我的友情链接
查看>>
ssh配置公钥私钥(key)登录SecureCRT
查看>>
go 字符串长度为空的判断 效率
查看>>
openstack安装(liberty)--安装认证服务(Identity service)
查看>>
邮件服务器软件为企业分支“搭桥”
查看>>
Windows Azure之VM的迁移之旅
查看>>
DevOps系列——Gogs和Jenkins的CI配置
查看>>
ExtJS4.2学习(php版)
查看>>
负载均衡——HAProxy
查看>>
win7 访问本机的虚拟机中centos的web项目
查看>>
批处理之播放文本文件
查看>>
windows server 2008活动目录的备份与还原
查看>>
spring boot 2.0.1.RELEASE hibernate 缓存 ehcache 详解
查看>>
关于windows7的更新update失败,windows media play安装失败的 ...
查看>>