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. 树(tree)

    树(tree) 题目描述 小明正在研究一种砍树游戏.一开始在W列H行的方格上,每一个格子都长着一颗树,格子的行从北到南依次编号,格子的列从西到东依次编号. 小明会砍倒一些树,每砍倒一颗树,树会占据这个 ...

  2. ACM-Hero In Maze

                                                   Hero In Maze 时间限制(普通/Java):1000MS/10000MS          运行 ...

  3. 追加window.onload函数

    function addLoadEvent(func) { var old_onload = window.onload; if (typeof window.onload != 'function' ...

  4. poj 3071 Football (概率DP水题)

    G - Football Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit ...

  5. Hoof, Paper, Scissors(USACO)

    题目大意: 一种游戏(类似于石头剪刀布):两个人分别给出一个字母,然后比较:H>S,S>P,P>H,我们已知对手的字母顺序,求在前n局中我们最多能赢多少次. 由于出字母的人非常懒,所 ...

  6. 有关cookie的内容

    包括: Cookie概述(Cookie的存放,有效期和作用域) Cookie操作(保存Cookie,读取Cookie,Cookie的生命周期) Cookie工作原理(Cookie与会话跟踪,Cooki ...

  7. 3.Centos-Docker-rancher

    1.安装mysql,设置密码 docker run -d --name mysqldb -e MYSQL_ROOT_PASSWORD=密码 mysql:latest --character-set-s ...

  8. log4j2配置文件动态指定日志文件名称

    按照习惯性思维,log4j2中xml中取系统属性应该和log4j一样,但是结果却并不是我们想的这样,存在一定的差别,log4j中的配置可参考 log4j配置文件动态指定日志文件名称 . 现在就来看看到 ...

  9. ubuntu16下安装telnet和opensshserver

    安装了虚拟机,使用的是ubuntu 16,server版本. 启动后发现没有telnet和ssh,就安装了(netstat -a|grep telnet). apt-get install openb ...

  10. [thinkphp] APP_DEBUG开启之后session不稳定

    有时候在一个方法中写入了session, 在另一个方法中打印却什么都没有. 我这次的情况是网页上通过ajax传值到一个php函数,然后php把值写入session中.然后我在另一个php方法中打印se ...