Java ArrayList&Vector 源代码分析
2016-03-13 09:53
796 查看
ArrayList 对象继承了AbstractList对象,这就是说,ArrayList可以使用迭代器来操作,但是有一点要注意,上一次我们看AbstractList源代码时,知道这个迭代器是快速失败的,也就是说他记录了修改的次数,因此在实现这个ArrayList的添加操作时,我们也需要随时更新这个操作记录。也就是如下这个变量。
现在从头开始看ArrayList对象的使用。
第一步是构造函数,我们经常使用如下语句来初始化对象。
实际上调用的构造函数如下:
这里使用了另外一个构造函数,
这个构造函数就是初始化了一个Object数组。并在初始化之前检查了参数范围。我们平时使用的时候,默认都是10.
现在看一下最常用的添加元素操作,源代码如下:
这里添加逻辑很简单,就是在数组末尾添加了这个元素。但是添加之前,有一个ensureCapacityInternal操作,这个操作就很重要了。我们看看做什么的?
这里首先给父类的modCount变量加1,就是说这个容器又多了一个操作,这个主要用于检查多线程环境来使用的。
另外,这里JDK代码中注释了一句overflow-conscious code,检查了,如果数组末尾的这个下标已经超出了数组长度的话,那么就需要扩展数组的长度了。这里使用了grow方法。
这个方法逻辑就是扩展数组的长度,长度扩展逻辑是旧的容量除2,再加上以前的容量,换句话说就是一半一半增加的。这里又判断,如果新的容量还是不够的话,那么就直接将最新的数组下标值赋值给数组长度,再进一步检查,新的容量是否比Integer.MAX_VALUE-8还要大,这里为什么是这个数值,JDK的源码中注释了如下说明:
是说有一些JVM会保存一些头字段存放在数组中。如果清理比这个更大的数组,就会导致内存溢出。所以这里是JVM的限制了。
所以整个添加操作到这里基本就结束了。
看一下删除操作:
这里简单说明下删除的逻辑:
判断数组下标的范围是否超过数组容量。
增加操作的记录。
检查删除的元素是否在数组末尾,如果不是,则将index后的元素迁移。
最后一步很重要,就是将删除元素所留下来的位置,置为null,方便GC工作。
这里简单说一句,将变量置为null,我以前一直以为是纯属装B的工作,变量过了作用域自然会被GC清理,但是这里却也这么做了,究其原因,是因为这是数组,不是一个简单的变量。
说明一下Vector对象的实现,这个对象也是实现了AbstractList接口,实现和ArrayList一样,但是有一点不同,就是Vector对象中所有的操作接口都是用了synchronized关键字,也就是说每个操作都给Vector对象实例加锁了。这个和StringBuilder&StringBuffer的差异是一样的。
protected transient int modCount = 0;
现在从头开始看ArrayList对象的使用。
第一步是构造函数,我们经常使用如下语句来初始化对象。
List<Object> list = new ArrayList<Object>();
实际上调用的构造函数如下:
public ArrayList() { this(10); }
这里使用了另外一个构造函数,
public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; }
这个构造函数就是初始化了一个Object数组。并在初始化之前检查了参数范围。我们平时使用的时候,默认都是10.
现在看一下最常用的添加元素操作,源代码如下:
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
这里添加逻辑很简单,就是在数组末尾添加了这个元素。但是添加之前,有一个ensureCapacityInternal操作,这个操作就很重要了。我们看看做什么的?
private void ensureCapacityInternal(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
这里首先给父类的modCount变量加1,就是说这个容器又多了一个操作,这个主要用于检查多线程环境来使用的。
另外,这里JDK代码中注释了一句overflow-conscious code,检查了,如果数组末尾的这个下标已经超出了数组长度的话,那么就需要扩展数组的长度了。这里使用了grow方法。
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); }
这个方法逻辑就是扩展数组的长度,长度扩展逻辑是旧的容量除2,再加上以前的容量,换句话说就是一半一半增加的。这里又判断,如果新的容量还是不够的话,那么就直接将最新的数组下标值赋值给数组长度,再进一步检查,新的容量是否比Integer.MAX_VALUE-8还要大,这里为什么是这个数值,JDK的源码中注释了如下说明:
/** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
是说有一些JVM会保存一些头字段存放在数组中。如果清理比这个更大的数组,就会导致内存溢出。所以这里是JVM的限制了。
所以整个添加操作到这里基本就结束了。
看一下删除操作:
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work return oldValue; }
这里简单说明下删除的逻辑:
判断数组下标的范围是否超过数组容量。
增加操作的记录。
检查删除的元素是否在数组末尾,如果不是,则将index后的元素迁移。
最后一步很重要,就是将删除元素所留下来的位置,置为null,方便GC工作。
这里简单说一句,将变量置为null,我以前一直以为是纯属装B的工作,变量过了作用域自然会被GC清理,但是这里却也这么做了,究其原因,是因为这是数组,不是一个简单的变量。
说明一下Vector对象的实现,这个对象也是实现了AbstractList接口,实现和ArrayList一样,但是有一点不同,就是Vector对象中所有的操作接口都是用了synchronized关键字,也就是说每个操作都给Vector对象实例加锁了。这个和StringBuilder&StringBuffer的差异是一样的。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树