008 Java集合浅析3
在前面的几节里,本教程从整体架构上去把握了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的更多相关文章
- 009 Java集合浅析4
前面一篇教程中,我们分析了List派别中的最常见也最重要的一个类ArrayList<E>.从我们的分析来看,ArrayList作为动态数组的模拟,使用的是连续内存空间来存储数据,带来了可随 ...
- Java集合框架之TreeMap浅析
Java集合框架之TreeMap浅析 一.TreeMap综述: TreeMap在Map中的结构如下:
- Java集合框架之HashMap浅析
Java集合框架之HashMap浅析 一.HashMap综述: 1.1.HashMap概述 位于java.util包下的HashMap是Java集合框架的重要成员,它在jdk1.8中定义如下: pub ...
- Java集合框架之Map接口浅析
Java集合框架之Map接口浅析 一.Map接口综述: 1.1java.util.Map<k, v>简介 位于java.util包下的Map接口,是Java集合框架的重要成员,它是和Col ...
- Java集合框架之TreeSet浅析
Java集合框架之TreeSet浅析 一.TreeSet综述: 1.1TreeSet简介: TreeSet是Java集合框架的重要成员,先来看看TreeSet在jdk1.8中的定义吧: public ...
- Java集合框架之HashSet浅析
Java集合框架之HashSet浅析 一.HashSet综述: 1.1HashSet简介 位于java.util包下的HashSet是Java集合框架的重要成员,它在jdk1.8中定义如下: publ ...
- Java集合框架之Set接口浅析
Java集合框架之Set接口浅析 一.java.util.Set接口综述: 这里只对Set接口做一简单综述,其具体实现类的分析,朋友们可关注我后续的博文 1.1Set接口简介 java.util.se ...
- Java集合框架之Vector浅析
Java集合框架之Vector浅析 一.Vector概述: 位于java.util包下的Vector是Java集合框架的重要一员,虽然没有ArrayList那么的常用,但是我们还要对其做相关学习: 1 ...
- Java集合框架之LinkedList浅析
Java集合框架之LinkedList浅析 一.LinkedList综述: 1.1LinkedList简介 同ArrayList一样,位于java.util包下的LinkedList是Java集合框架 ...
随机推荐
- leetcode难度及面试频率
转载自:LeetCode Question Difficulty Distribution 1 Two Sum 2 5 array sort set ...
- win8 64位+Oracle 11g 64位下使用PL/SQL Developer 的解决办法
1)安装Oracle 11g 64位2)安装32位的Oracle客户端( instantclient-basic-win32-11.2.0.1.0)下载 instantclient-basic-win ...
- UED大全
http://www.baiduux.com/ 百度UFOhttp://ued.sohu.com/ 搜狐UEDhttp://ued.taobao.com/ 淘宝UEDhttp://www.ued ...
- Bootstrap入门(十四)组件8:媒体对象
Bootstrap入门(十四)组件8:媒体对象 这是一个抽象的样式,用以构建不同类型的组件,这些组件都具有在文本内容的左或右侧对齐的图片(就像博客评论或 Twitter 消息等). 1.基本样式 2. ...
- TCP协议滑动窗口(一)——控制数据传输速率
窗口大小:TCP头中一个16位的域,表示当前可用接受缓冲区大小.在每个TCP对等段连接初始化时,告诉对方自己的窗口大小(不一定是满额,假如满额65201字节,可能暂时通告5840字节).若客户端接受数 ...
- 多线程方式实现Socket通信
一.首先,介绍下两类传输协议:TCP:UDP TCP是Tranfer Control Protocol的 简称,是一种面向连接的保证可靠传输的协议.通过TCP协议传输,得到的是一个顺序的无差错的数据流 ...
- 使用net Manager工具远程连接oracle
一,在服务端配置oracle端口 在命令行中输入netca命令,打开相关配置默认端口号为1521 二,配置端口后使用Telnet工具调试端口是否联通 在命令行输入telnet 服务器ip 端口号 三, ...
- Vue.js 系列教程 ②
这是关于 JavaScript 框架 Vue.js 五个教程的第二部分.在这一部分,我们将学习组件,Props 以及 Slots.这不是一个完整的指南,而是基础知识的概述,所以你可以了解Vue.js ...
- UI进阶 即时通讯之XMPP登录、注册
1.XMPP环境搭建 http://www.cnblogs.com/fearlessyyp/p/5506644.html 第一次打开可能会有点儿慢,图片很多,步骤很详细,祝搭建成功. 2.工程中添加X ...
- Jasmine基础语法
简介 Jasmine 是JavaScript的测试框架,它不依赖其他框架,也不依赖DOM,更重要的是它语法简单.以下实例都是基于Jasmine 2.5.2的,并且来自官网:https://jasmin ...