在前面的几节里,本教程从整体架构上去把握了JDK中的集合框架,并简单分析了其中Collection组的顶级接口,知道Collection接口的常见直接子接口有List、Set和Queue,并就这三个子接口的独有特性进行了简单地分析和比较。

本篇教程将会对实际编程中使用最频繁、最简单地ArrayList进行讲解,我先给出一个该类的类层次结构图。

从上面的结构图可以看出,集合框架的主题架构其实是以类为主体的,而不是接口。如果知道类、抽象类和接口之间的区别与联系,就能明白这里引入抽象类的原因。简单地说,如果不引入抽象类,每个具有Collection接口属性的类别都必须自己去实现该接口中的10多个方法,通过让类别直接继承自AbstractCollection这个抽象类,去除了重复代码。

首先,我们来看一下AbstractCollection这个抽象类的部分源码,如下:

public abstract class AbstractCollection<E> implements Collection<E> {

public boolean isEmpty() {

return size() == 0;

}

public boolean add(E e) {

throw new UnsupportedOperationException();

}

}

此抽象类就是Collection接口的实现,并实际实现了其中的某些方法,而且作为一个抽象类是不能直接用来实例化对象。

其次,再看看AbstractList这个抽象类的部分源码,如下:

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {

public boolean add(E e) {

add(size(), e);

return true;

}

}

此抽象类继承了AbstractCollection抽象类,并且实现了List接口,这就使得此类同时具备了普通Collection和特定的List类型集合的属性。注意:此类的subList方法返回的List<E>类型是两个内部类中的一个,要么是RandomAccessSubList<E>,要么是SubList<E>。

最后,我们再来仔细看看ArrayList的源码,部分源码如下:

public class ArrayList<E> extends AbstractList<E>

implements List<E>, RandomAccess, Cloneable, java.io.Serializable

{

private static final int DEFAULT_CAPACITY = 10;

private transient Object[] elementData;

}

这个ArrayList作为一个非抽象方法,已经实现了抽象父类和父接口中的所有方法。整体来说,并没有太多新的方法和特性加入,但其中值得关注的点不少,现在我们就来一一剖析。

第一个不寻常的地方在于ArrayList实现了Serializable接口,但是该类的重要成员elementData却使用transient修饰符。transient修饰符的作用正是让被其修饰的变量不参与正常的序列化中,那么ArrayList是如何实现Serializable的呢?秘密藏在该类的两个方法:

private void writeObject(java.io.ObjectOutputStream s)

throws java.io.IOException{

}

private void readObject(java.io.ObjectInputStream s)

throws java.io.IOException, ClassNotFoundException {

}

我们知道对于实现了Serializable接口的类,我们可以直接使用ObjectOutputStream和ObjectInputStream进行对象的序列化和反序列化。当我们的类中包含有这两个方法时,ObjectOutputStream的writeObject方法就会调用类中的writeObject方法。注意:如果的看得够仔细,你会发现writeObject方法是私有的,同时该方法在Object中并不存在,那么ObjectOutputStream作为外部类是如何找到writeObject方法并调用成功的呢?如果你知道Java的反射机制的话,我相信你可能已经猜到了,此处正是使用的反射机制。这里之所以如何操作是为了效率,毕竟只需要序列化elementData中size大小的变量,而不是capacity大小的变量。

第二个不寻常的地方在于ArrayList类的clone()方法,从类的定义中可以看到该类实现了Cloneable接口,根据Cloneable接口的语义,ArrayList类实现了自己的clone()方法,我们看一下源码:

public Object clone() {

try {

@SuppressWarnings("unchecked")

ArrayList<E> v = (ArrayList<E>) super.clone();

v.elementData = Arrays.copyOf(elementData, size);

v.modCount = 0;

return v;

} catch (CloneNotSupportedException e) {

// this shouldn't happen, since we are Cloneable

throw new InternalError();

}

}

源码面前无秘密,可以看到方法中对于保存ArrayList内部数据的数组变量elementData是做了复制的,但是并没有针对数组变量本身再进行递归复制,所以在注释中写明了此方法是一个浅复制。

第三个不寻常的地方在于ArrayList类中含有一个名为modCount的变量,名字其实是modifyCount的缩写,表示ArrayList对象被修改的次数。而且这个modCount记录的只是修改size变量的次数,对于ArrayList对象中变量的替换是不会改变modCount的值。那么ArrayList类为什么要辛苦地去维护这个modCount变量值呢?如果你写过遍历ArrayList集合的同时去删除集合中某些元素的代码,并且你不是使用的迭代器方式的话,那么你一定遇到过ConcurrentModificationException异常。背后的原理就在于remove方法会修改modCount的值,而整个foreach循环过程不允许modCount值发生改变。

第四点来看一下ArrayList的内存数组增长机制,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);

}

从源码可以出到,通常情况下就是一个50%的增长幅度,但也可以直接设定具体增长的目标值。同时,某些JVM实际可分配的最大数组大小并不能达到Integer.MAX_VALUE,而是Integer.MAX_VALUE - 8。

关于ArrayList<E>还有比较多的知识点,限于篇幅就不一一讲解了,像其中迭代器模式的实现,内部类的使用等就暂时先跳过啦,以后以机会再专门讲解。最后提一下ArrayList<E>实现的RandomAccess这个接口,它是一个标记接口,本身不包含任何成员,用来标记某些集合类具有随机访问性,从而在应用泛型算法可以考虑利用该特点来提高算法性能。

本系列文档会在本人的微信公众号发布,欢迎大家扫码关注。

                

008 Java集合浅析3的更多相关文章

  1. 009 Java集合浅析4

    前面一篇教程中,我们分析了List派别中的最常见也最重要的一个类ArrayList<E>.从我们的分析来看,ArrayList作为动态数组的模拟,使用的是连续内存空间来存储数据,带来了可随 ...

  2. Java集合框架之TreeMap浅析

    Java集合框架之TreeMap浅析 一.TreeMap综述: TreeMap在Map中的结构如下:

  3. Java集合框架之HashMap浅析

    Java集合框架之HashMap浅析 一.HashMap综述: 1.1.HashMap概述 位于java.util包下的HashMap是Java集合框架的重要成员,它在jdk1.8中定义如下: pub ...

  4. Java集合框架之Map接口浅析

    Java集合框架之Map接口浅析 一.Map接口综述: 1.1java.util.Map<k, v>简介 位于java.util包下的Map接口,是Java集合框架的重要成员,它是和Col ...

  5. Java集合框架之TreeSet浅析

    Java集合框架之TreeSet浅析 一.TreeSet综述: 1.1TreeSet简介: TreeSet是Java集合框架的重要成员,先来看看TreeSet在jdk1.8中的定义吧: public ...

  6. Java集合框架之HashSet浅析

    Java集合框架之HashSet浅析 一.HashSet综述: 1.1HashSet简介 位于java.util包下的HashSet是Java集合框架的重要成员,它在jdk1.8中定义如下: publ ...

  7. Java集合框架之Set接口浅析

    Java集合框架之Set接口浅析 一.java.util.Set接口综述: 这里只对Set接口做一简单综述,其具体实现类的分析,朋友们可关注我后续的博文 1.1Set接口简介 java.util.se ...

  8. Java集合框架之Vector浅析

    Java集合框架之Vector浅析 一.Vector概述: 位于java.util包下的Vector是Java集合框架的重要一员,虽然没有ArrayList那么的常用,但是我们还要对其做相关学习: 1 ...

  9. Java集合框架之LinkedList浅析

    Java集合框架之LinkedList浅析 一.LinkedList综述: 1.1LinkedList简介 同ArrayList一样,位于java.util包下的LinkedList是Java集合框架 ...

随机推荐

  1. 递归求和1到n

    一般的方法 #include<stdio.h> int sum(int n){ if(n==1) return 1; else return n+sum(n-1);} int main(v ...

  2. javaweb入门实例---servlet例子

    1.编写servlet: TreeDataServlet.java package com.maggie.tree; import java.io.IOException; import javax. ...

  3. SuperSocket入门(二)- 探索AppServer、AppSession,Conmmand和App.config

          在上一篇文章中,我们已经了解到了如何在SuperSocket处理客户端请求. 同时我们可能会发现一个问题,如果我们的服务器端包含有很多复杂的业务逻辑,这样的switch/case代码将会很 ...

  4. 【长 PI】

    /* 长 PI 说明: 圆周率后的小数位数是无止境的,如何使用电脑来计算这无止境的小数是一些数学家与程式设计师所感兴趣的,在这边介绍一个公式配合 大 数运算,可以计算指定位数的圆周率. 解法 : 首先 ...

  5. MyBatis 源码分析——动态代理

    MyBatis框架是如何去执行SQL语句?相信不只是你们,笔者也想要知道是如何进行的.相信有上一章的引导大家都知道SqlSession接口的作用.当然默认情况下还是使用DefaultSqlSessio ...

  6. WebServerice的发布

    在webserverice一文中,我们简单的介绍了一下什么是webserverice,以及如何建立一个webserverice服务.今天,我们一起学习下webserverice是如何发布的. 为什么要 ...

  7. Apache工作原理

                            Apache工作原理 在开始了解Apache前,我们先熟悉一下web服务器,因为apache也是web服务器的一种. Web系统由客户端(浏览器)和服务 ...

  8. 一个普通底层.NET程序员关于职场瓶颈期的思考,辗转自我提升/跳槽/转行之间

    徒有工龄,没技术没学历没平台没家底,工作几年,无车无房无存款还前景不明. 时常有身边的亲友问怎么学开发怎么转互联网,说起IT行业都说工资高,动辄月薪上万动辄年薪几十万. 再看看自己,我可能是假的程序员 ...

  9. 初学NodeJs

    1.在ecmascript部分node和js其实是一样的,比如数据类型的定义,语法结构,内置对象 在js中的顶层对象 window 在node中的顶层对象 global 注意:在node中也没有什么w ...

  10. HTML5 DOM扩展

    一.选择符 1. querySelector()方法:返回与该模式匹配的第一个元素 //取得body元素 var body = document.querySelector("body&qu ...