Java 集合 - Vector 和 Stack
前面写了一篇关于的是 LinkedList 的除了它的数据结构稍微有一点复杂之外,其他的都很好理解的。这一篇说的大家在开发中很少去用到,有的时候也可能是会用到的,了解就行。
注意在学习这一篇之前,需要有多线程的知识:
锁机制:对象锁、方法锁、类锁
对象锁就是方法锁:就是在一个类中的方法上加上 Synchronized 关键字,这就是给这个方法加锁了。
类锁:锁的是整个类,当有多个线程来声明这个类的对象的时候将会被阻塞,直到拥有这个类锁的对象被销毁或者主动释放了类锁。这个时候在被阻塞住的线程被挑选出一个占有该类锁,声明该类的对象。 其他线程继续被阻塞住。例如:在类 A 上有关键字 Synchronized,那么就是给类 A 加了类锁,线程 1 第一个声明此类的实例,则线程 1 拿到了该类锁,线程 2 在想声明类A的对象,就会被阻塞。
在本文中,使用的是方法锁。
每个对象只有一把锁,有线程 A,线程 B,还有一个集合 C 类,线程 A 操作 C 拿到了集合中的锁(在集合 C 中有用 Synchronized 关键字修饰的),并且还没有执行完,那么线程 A 就不会释放锁,当轮到线程 B 去操作集合 C 中的方法时,发现锁被人拿走了,所以线程 B 只能等待那个拿到锁的线程使用完,然后才能拿到锁进行相应的操作。
# Vector概述
通过 API 中可以知道:
- Vector 是一个可变化长度的数组
- Vector 增加长度通过的是 capacity 和 capacityIncrement 这两个变量,目前还不知道如何实现自动扩增的,等会源码分析
- Vector 也可以获得 iterator 和 listIterator 这两个迭代器,并且他们发生的是 fail-fast,而不是 failsafe,注意这里,不要觉得这个 Vector 是线程安全就搞错了,具体分析在下面会说
- Vector 是一个线程安全的类,如果使用需要线程安全就使用 Vector,如果不需要,就使用 ArrayList
- Vector 和 ArrayList 很类似,就少许的不一样,从它继承的类和实现的接口来看,跟 ArrayList 一模一样
注意:Java1.5 推出的 java.uitl.concurrent
包,为了解决复杂的并发问题的。所以开发中,不建议用 Vector,原因在 List 总结 会有解释,需要线程安全的集合类直接用 java.util.concurrent
包下的类。
# Vector源码分析
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
}
2
3
4
5
6
我们发现 Vector 的继承关系和层次结构和 ArrayList 中的一模一样,忘记的可以去 ArrayList 文章查看。
# 构造方法
一共有四个构造方法。
构造方法作用:
- 初始化存储元素的容器,也就是数组,elementData,
- 初始化 capacityIncrement 的大小,默认是 0,这个的作用就是扩展数组的时候,增长的大小,为 0 则每次扩展 2 倍
Vector():空构造
/**
* Constructs an empty vector so that its internal data array
* has size {@code 10} and its standard capacity increment is
* zero.
*/
///看注释,这个是一个空的Vector构造方法,所以让他使用内置的数组,这里还不知道什么是内置的数组,看它调用了自身另外一个带一个参数的构造器
public Vector() {
this(10);
}
2
3
4
5
6
7
8
9
Vector(int)
/**
* Constructs an empty vector with the specified initial capacity and
* with its capacity increment equal to zero.
*
* @param initialCapacity the initial capacity of the vector
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
//注释说,给空的cector构造器用和带有一个特定初始化容量用的,并且又调用了另外一个带两个参数的构造器,并且给容量增长值(capacityIncrement=0)为0,查看vector中的变量可以发现capacityIncrement是一个成员变量
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
2
3
4
5
6
7
8
9
10
11
12
13
ector(int,int)
/**
* Constructs an empty vector with the specified initial capacity and
* capacity increment.
*
* @param initialCapacity the initial capacity of the vector
* @param capacityIncrement the amount by which the capacity is
* increased when the vector overflows
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
//构建一个有特定的初始化容量和容量增长值的空的Vector,
public Vector(int initialCapacity, int capacityIncrement) {
super();//调用父类的构造,是个空构造
if (initialCapacity < 0)//小于0,会报非法参数异常:不合法的容量
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];//elementData是一个成员变量数组,初始化它,并给它初始化长度。默认就是10,除非自己给值。
this.capacityIncrement = capacityIncrement;//capacityIncrement的意思是如果要扩增数组,每次增长该值,如果该值为0,那数组就变为两倍的原长度,这个之后会分析到
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Vector(Collection c)
/**
* Constructs a vector containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this
* vector
* @throws NullPointerException if the specified collection is null
* @since 1.2
*/
//将集合c变为Vector,返回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);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 核心方法
add() 方法
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
//就是在vector中的末尾追加元素。但是看方法,synchronized,明白了为什么vector是线程安全的,因为在方法前面加了synchronized关键字,给该方法加锁了,哪个线程先调用它,其它线程就得等着,如果不清楚的就去看看多线程的知识,到后面我也会一一总结的。
public synchronized boolean add(E e) {
modCount++;
//通过arrayList的源码分析经验,这个方法应该是在增加元素前,检查容量是否够用
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ensureCapacityHelper(int)
/**
* This implements the unsynchronized semantics of ensureCapacity.
* Synchronized methods in this class can internally call this
* method for ensuring capacity without incurring the cost of an
* extra synchronization.
*
* @see #ensureCapacity(int)
*/
////这里注释解释,这个方法是异步(也就是能被多个线程同时访问)的,原因是为了让同步方法都能调用到这个检测容量的方法,比如add的同时,另一个线程调用了add的重载方法,那么两个都需要同时查询容量够不够,所以这个就不需要用synchronized修饰了。因为不会发生线程不安全的问题
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//容量不够,就扩增,核心方法
grow(minCapacity);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
grow(int)
//看一下这个方法,其实跟arrayList一样,唯一的不同就是在扩增数组的方式不一样,如果capacityIncrement不为0,那么增长的长度就是capacityIncrement,如果为0,那么扩增为2倍的原容量
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);
}
2
3
4
5
6
7
8
9
10
11
12
13
只要能看的懂 ArrayList,这个就是在每个方法上比 ArrayList 多了一个 Synchronized,其他都一 样。这里就不再分析了。
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
2
3
4
5
6
# Stack
现在来看看 Vector 的子类 Stack,学过数据结构都知道,这个就是栈的意思。那么该类就是跟栈的用法一样了
class Stack<E> extends Vector<E> {}
通过查看他的方法,和查看 API 文档,很容易就能知道他的特性。就几个操作,出栈,入栈等,构造方法也是空的,用的还是数组,父类中的构造,跟父类一样的扩增方式,并且它的方法也是同步的,所以也是线程安全。
# 总结Vector和Stack
Vector 总结(通过源码分析)
- Vector 线程安全是因为它的方法都加了 Synchronized 关键字
- Vector 的本质是一个数组,特点能是能够自动扩增,扩增的方式跟 capacityIncrement 的值有关
- 它也会 fail-fast,还有一个 fail-safe 两个的区别在下面的 List 总结中会讲到
Stack 的总结
- 对栈的一些操作,先进后出
- 底层也是用数组实现的,因为继承了 Vector
- 也是线程安全的