Java源码阅读Vector
1类签名与注释
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
Vector类实现了一个可伸缩的对象数组。和数组一样,他里面的组件能被integer型的索引访问。不同的是,Vector被创建后,当增加或删除项的时候,其size可以增长或收缩来适应大小。
每个Vector都试图通过保持capacity和capacityIncrement来优化存储管理。 capacity总是至少和vector的size一样大,它通常是更大的因为组件添加到Vector,Vector的存储块的大小增加“capacityincrement”。应用程序可以在插入大量组件之前增加Vector的capacity,这减少了增量再分配的数量。
通过该类的iterator()和listIterator(int)方法返回的iterator是fail-fast(快速失败机制)的:
当iterator被创建后,如果vector的结构在任何时候被修改,除了使用iterator自己的ListIterator#remove()或者ListIterator#add(Object)外的任何方法,将会抛出ConcurrentModificationException异常。因此,面对并发修改时,迭代器会快速而干净地失败,而不是在未来的某个不确定的时间进行有风险的行为。被elements()方法返回的Enumeration不是fail-fast的。
值得注意的是fail-fast行为可能是不能得到保证的。因此,编写一个依赖于ConcurrentModificationException异常的程序是错误的:迭代器的fail-fast行为应该只用于检测错误。
在Java 2版本中,这个类被修改为实现List接口,使得它成为java集合框架成员。Vector是synchronized的(线程安全的),如果不需要线程安全的实现,推荐使用ArrayList。(效率更高)
2关于容量(capacity)和size的问题
capacity:是指这个Vector能放多少组件,是指数组分配的大小(elementData.length)
size:是指这个Vector里面放了多少组件,用elementCount表示。
下面两个方法分别获取Vector的capacity和size:
public synchronized int capacity() {
return elementData.length;
} public synchronized int size() {
return elementCount;
}
3构造函数
//capacityIncrement表示每次扩容的增量,若增量大于0,则每次增加增量大小,否则每次扩容为原来的2倍
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
} public Vector(int initialCapacity) {
this(initialCapacity, 0);
} // 默认的容量为10,增量为0
public Vector() {
this(10);
} //将Collection集合传进来,构造vector
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
4容量的可伸缩性
4.1扩容操作
public synchronized void ensureCapacity(int minCapacity) {
if (minCapacity > 0) {
modCount++;
ensureCapacityHelper(minCapacity);
}
} private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
} private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
} private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
通过调用ensureCapacity(minCapacity)进入扩容操作,这里有个minCapacity参数,表示所需的最小容量,具体怎么用请看接下来的代码。
(1)ensureCapacity方法首先判断minCapacity大于0,若是的话进行下一步modCount++,modCount是继承自父类AbstractList的变量,记录集合“结构修改”操作的次数,这种操作主要包括size的改变等。然后调用ensureCapacityHelper(minCapacity)方法。
(2)ensureCapacityHelper方法先判断minCapacity是否大于当前Vector的容量,若小于,则不需要扩容,否则调用真正的扩容方法grow(minCapacity)。
(3)grow方法先拟定一个新容量newCapacity,若增量大于0则newCapacity等于旧容量加增量,否则等于2倍旧容量。然后将newCapacity和minCapacity的较大值赋给newCapacity(newCapacity = Max(newCapacity,minCapacity))。接下来判断若newCapacity小于MAX_ARRAY_SIZE,就将旧数组复制到容量为newCapacity的新数组中。反之,调用hugeCapacity(minCapacity)求newCapacity。
(4)hugeCapacity方法首先判断minCapacity是否小于0,若是,则内存溢出,抛出OutOfMemoryError错误。若否,则继续判断minCapacity是否大于MAX_ARRAY_SIZE,若是则返回Integer.MAX_VALUE,否则返回MAX_ARRAY_SIZE。(这里说明,其实vector的容量是可以达到整数最大值的,而不仅仅是整数最大值-8)
这里不太理解第4步为什么minCapacity小于0,就报内存溢出错误。既然容量可以达到整数最大值,那么8>=newCapacity - MAX_ARRAY_SIZE > 0这样不也没超过吗?为什么不用判断(小于等于8)就直接报错。
4.2容量收缩
Vector集合不仅可以扩容,还可以减容,就是把多余的空间给减掉。
public synchronized void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (elementCount < oldCapacity) {
elementData = Arrays.copyOf(elementData, elementCount);
}
}
oldCapacity获取的是数组的大小,也就是前面第二节说的Vector的容量。elementCount表示Vector里面实际放了多少个元素。当elementCount < oldCapacity的时候就有部分空间没有存放数据,这里通过将旧数组复制到新数组上来完成的。(数组的长度不可变)。
4.3 setSize方法
setSize(int newSize)方法功能如下:若newSize大于当前容量,将Vector扩容到newSize。否则,多余的元素变为null,但是Vector的容量不变(数组实现,有序)。
public synchronized void setSize(int newSize) {
modCount++;
if (newSize > elementCount) {
ensureCapacityHelper(newSize);
} else {
for (int i = newSize ; i < elementCount ; i++) {
elementData[i] = null;
}
}
elementCount = newSize;
}
5查找
5.1查找元素
查找一个元素是否在Vector集合里面使用contains(Object o)方法。
public boolean contains(Object o) {
return indexOf(o, 0) >= 0;
} public int indexOf(Object o) {
return indexOf(o, 0);
} public synchronized int indexOf(Object o, int index) {
if (o == null) {
for (int i = index ; i < elementCount ; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = index ; i < elementCount ; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
contains方法内部调用indexOf(Object o,0)方法,indexOf内部就是简单的for循环遍历数组,若找到了返回元素的索引标号(Vector是允许元素重复的,这里返回的是第一个匹配的索引标号),否则返回-1。
5.2查找匹配元素的最后1个索引号
前面说过,可能有多个元素匹配查找,若找找出最后一个匹配的索引号使用lastIndexOf方法:
public synchronized int lastIndexOf(Object o) {
return lastIndexOf(o, elementCount-1);
} public synchronized int lastIndexOf(Object o, int index) {
if (index >= elementCount)
throw new IndexOutOfBoundsException(index + " >= "+ elementCount); if (o == null) {
for (int i = index; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = index; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
和上述查找一样,但是数组遍历是从后往前的。
5.3其他查找方法
//查找index位置的元素
public synchronized E elementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
}
return elementData(index);
} //查找第一个元素
public synchronized E firstElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(0);
} //查找最后一个元素
public synchronized E lastElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(elementCount - 1);
}
6主要操作
6.1添加元素
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
} private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
这里是在集合尾部添加一个元素。有以下几个操作:(1)modCount++;(2)判断是否需要扩容;(3)在集合尾部添加一个元素。
下面是在具体的索引位置添加1个元素:
public void add(int index, E element) {
insertElementAt(element, index);
} public synchronized void insertElementAt(E obj, int index) {
modCount++;
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
ensureCapacityHelper(elementCount + 1);
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}
上述代码主要有以下几个操作:
(1)modCount++;
(2)判断索引位置是否越界;
(3)判断是否需要扩容;
(4)[index,elementCount]位置的数据复制到[index+1,elementCount+1];
(5)在index位置插入元素,然后elementCount加1
下面是将一个集合的所有元素添加到Vector集合里面:
public synchronized boolean addAll(Collection<? extends E> c) {
modCount++;
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityHelper(elementCount + numNew);
System.arraycopy(a, 0, elementData, elementCount, numNew);
elementCount += numNew;
return numNew != 0;
}
这里说明一下toArray()方法,该方法是声明在Collection接口里面的,也就是说所有的Collection的实现类都应该有该方法的实现。我们看一下Vector是怎么实现的吧:
public synchronized Object[] toArray() {
return Arrays.copyOf(elementData, elementCount);
}
6.2删除元素
public boolean remove(Object o) {
return removeElement(o);
} public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
} public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
删除元素首先是找到元素的位置index,然后调用removeElementAt(index)进行删除。
这里System.arraycopy(elementData, index + 1, elementData, index, j)是个native方法。把索引位后面的元素全部往前挪1位(底层是用复制实现的),最后1位不会改变,举个例子:
int[] a = { 1, 2, 3, 4, 5, 6 };
System.arraycopy(a, 3, a, 2, 3);
打印a得到{ 1, 2, 4, 5, 6 ,6}。所以源码最后1条要将数组末尾的值清空来方便GC。
删除指定位置的元素与上述removeElementAt方法类似:
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index); int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--elementCount] = null; // Let gc do its work return oldValue;
}
删除Vector中指定集合的所有元素:
public synchronized boolean removeAll(Collection<?> c) {
return super.removeAll(c);
}
调用的是父类AbstractCollection中的removeAll方法:
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<?> it = iterator();
while (it.hasNext()) {
if (c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
这里的删除是通过迭代器实现的,AbstractCollection中并没有具体实现iterator(),所以这里Vector是调用自己的iterator()方法:
public synchronized Iterator<E> iterator() {
return new Itr();
} private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount; public boolean hasNext() {
// Racy but within spec, since modifications are checked
// within or after synchronization in next/previous
return cursor != elementCount;
} public E next() {
synchronized (Vector.this) {
checkForComodification();
int i = cursor;
if (i >= elementCount)
throw new NoSuchElementException();
cursor = i + 1;
return elementData(lastRet = i);
}
} public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
synchronized (Vector.this) {
checkForComodification();
Vector.this.remove(lastRet);
expectedModCount = modCount;
}
cursor = lastRet;
lastRet = -1;
} //省略
}
Vector声明了一个内部类用来实现迭代器接口。具体的迭代器详情可以参考我的另一篇博客java 迭代器
补充:Vector可以通过listIterator()方法获得ListIterator对象(也是内部类实现),ListIterator接口继承了Iterator接口,不同的是前者可以双向遍历。
6.3get和set方法
get方法是取索引位置的元素,set方法是设置索引位置的元素:
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index); return elementData(index);
} public synchronized E set(int index, E element) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index); E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
7总结
由于篇幅与实力的问题(主要是后者),这里只贴了一些常用方法的代码,其他详情以及Java8的新加入内容有待深入。
完。
Java源码阅读Vector的更多相关文章
- Java源码阅读的真实体会(一种学习思路)
Java源码阅读的真实体会(一种学习思路) 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈 ...
- Java源码阅读的真实体会(一种学习思路)【转】
Java源码阅读的真实体会(一种学习思路) 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+ ...
- 如何阅读Java源码 阅读java的真实体会
刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比 ...
- java1.7集合源码阅读: Vector
Vector是List接口的另一实现,有非常长的历史了,从jdk1.0开始就有Vector了,先于ArrayList出现,与ArrayList的最大区别是:Vector 是线程安全的,简单浏览一下Ve ...
- [收藏] Java源码阅读的真实体会
收藏自http://www.iteye.com/topic/1113732 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我 ...
- Java源码阅读Stack
Stack(栈)实现了一个后进先出(LIFO)的数据结构.该类继承了Vector类,是通过调用父类Vector的方法实现基本操作的. Stack共有以下五个操作: put:将元素压入栈顶. pop:弹 ...
- java源码阅读Hashtable
1类签名与注释 public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, C ...
- Java源码阅读ArrayList
1简介 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAc ...
- Java源码阅读顺序
阅读顺序参考链接:https://blog.csdn.net/qq_21033663/article/details/79571506 阅读源码:JDK 8 计划阅读的package: 1.java. ...
随机推荐
- POJ 2406 Power Strings 暴力
emmmm 显然的是a串长度是s串长度的因数 我们可以暴力枚举因数然后暴力check #include<cstdio> #include<algorithm> #include ...
- 动态MST
原谅我真的写不下去了,太难写了,先占坑. 啥时候有比较连续的时间了再写 肯定没用了的无聊代码 #include <cstdio> #include <vector> const ...
- JSON.stringify与jQuery.parseJSON
1.JSON.stringify,这个函数的作用主要是为了系列化对象的.(或者说是将原来的对象转换为字符串的,如json对象): 首先定义一个json对象,var jsonObject = { &qu ...
- tips 前端 移动端 web iscroll 5 自译文档 api速查
iscroll 可以做的 1,模拟原生的ios 或者android等设备的元素滚动,app里的那种顺滑的滚动,仅仅使用一个轻量的js库实现(甚至更酷炫的视觉感受) 2,手机端流行的下拉刷新,ajax异 ...
- greasemonkey
Greasemonkey Hacks/Getting Started < Greasemonkey Hacks Greasemonkey Hacks Foreword Credits Prefa ...
- cat /proc/maps 进程内存映射【转】
转自:http://blog.csdn.net/fisher_jiang/article/details/5063852 proc/<PID>/maps查看进程的虚拟地址空间是如何使用的. ...
- 明远智睿IMX6Q Android4.4.2移植USBWIFI(RTL8188EUS)
移植过程中得到网友的不少帮助,很感谢!为了让更多的网友不像我这样折腾,特写此文以做参照.过程中主要参考< Realtek_Wi-Fi_SDK_for_Android_KK_4_4.pdf > ...
- Scala学习随笔——深入类和对象
函数化对象(又称方程化对象)指的是所定义的类或对象不包含任何可以修改的状态. 本篇随笔就是着重记录函数化对象.定义了一个有理数类定义的几个不同版本,以介绍 Scala 类定义的几个特性:类参数和构造函 ...
- nginx 根据url访问次数限制
#获取日期 date=`date +"%y%m%d"` #设置日志路径 data="/data/logs/abc.com/access.log" #配置文件路径 ...
- os.path.isfile的错误
今天写了一个程序,读取子目录(source)下的所有文件,然后转换. 程序一部分代码如下: def DtoTab(dsrc, dtarget): try: for item in os.listdir ...