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的更多相关文章

  1. Java源码阅读的真实体会(一种学习思路)

    Java源码阅读的真实体会(一种学习思路) 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈 ...

  2. Java源码阅读的真实体会(一种学习思路)【转】

    Java源码阅读的真实体会(一种学习思路)   刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+ ...

  3. 如何阅读Java源码 阅读java的真实体会

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心.   说到技术基础,我打个比 ...

  4. java1.7集合源码阅读: Vector

    Vector是List接口的另一实现,有非常长的历史了,从jdk1.0开始就有Vector了,先于ArrayList出现,与ArrayList的最大区别是:Vector 是线程安全的,简单浏览一下Ve ...

  5. [收藏] Java源码阅读的真实体会

    收藏自http://www.iteye.com/topic/1113732 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我 ...

  6. Java源码阅读Stack

    Stack(栈)实现了一个后进先出(LIFO)的数据结构.该类继承了Vector类,是通过调用父类Vector的方法实现基本操作的. Stack共有以下五个操作: put:将元素压入栈顶. pop:弹 ...

  7. java源码阅读Hashtable

    1类签名与注释 public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, C ...

  8. Java源码阅读ArrayList

    1简介 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAc ...

  9. Java源码阅读顺序

    阅读顺序参考链接:https://blog.csdn.net/qq_21033663/article/details/79571506 阅读源码:JDK 8 计划阅读的package: 1.java. ...

随机推荐

  1. js函数调用与声明 (for时注意)

    可以的: test();  // 直接function 方式声明的函数可以直接调用,后声明 function test(){} aa();   //error  var 方式声明的函数需先声明后调用v ...

  2. EF4学习链接

    原文发布时间为:2011-09-23 -- 来源于本人的百度文章 [由搬家工具导入] 1.Fluent API 的方式定义与数据库映射 2.利用特性实现与数据库的映射 3.EF的一些公约的介绍 4.E ...

  3. 不只是内存分析工具~valgrind

    体系结构:原理介绍·参考好文:应用 Valgrind 发现 Linux 程序的内存问题 简单组一个摘要: Valgrind包括如下一些工具: Memcheck.这是valgrind应用最广泛的工具,一 ...

  4. ios截屏代码[转]

    http://www.cnblogs.com/chenxiangxi/p/3547974.html 这位博主的连接中将ios自定义大小位置的截屏代码写的很不错,马上就能用的方法,对于只想马上用的程序员 ...

  5. Linux下Redis使用

    1. 简介 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统. 2. 安装 安装方法如下: # yum i ...

  6. 解决provisional headers are shown问题

    前端通过ajax的get请求向后端发送数据,后端死活收不到. ajax代码如下: $.ajax({ url:"/news_list", type:"GET", ...

  7. hdu 3264(枚举+二分+圆的公共面积)

    Open-air shopping malls Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/ ...

  8. PHP成生若干位防伪码的方法

    public static function createCode($length =16) { $arr = array_merge(range(0, 9), range('a', 'z')); / ...

  9. virtualbox centos6.6 minimal 与宿主机win7共享文件夹

    1.virtualbox, 设置-共享文件夹,设置好共享文件夹win7dir 2.centos 创建文件夹比如share 3.mount -t vboxsf win7dir /root/share 4 ...

  10. 信息批量提取工具bulk-extractor

    信息批量提取工具bulk-extractor   在数字取证中,通常需要面对海量的数据,如几百GB甚至TB级别的数据.从这些海量数据中,提取有价值的数据是一个漫长.枯燥.繁琐的过程.Kali Linu ...