Java容器专栏: Java容器源码详细解析(面试知识点)
因为部分内容可能已经在本专栏前面的文章提及到,这里只是简单说明,所以建议先看看:
Vector
Vector这个类现在已经不常用了,快被遗弃了。
(一)Vector底层数据结构
和ArrayList一样,底层都是Object动态数组。(向量队列)
(二)Vector继承和实现关系
(和ArrayList一样,在ArrayList中已有说明,这里不做过多的解释)
1、实现的接口:
List接口 、 Cloneable接口 、 Serializable接口 、 RandomAccess接口
2、继承的抽象类:
AbstractList抽象类
(三)Vector源码分析
1、重要的成员变量
//存储元素的数组,向量中最后一个元素后面的任何元素都是null
protected Object[] elementData;
//实际元素个数
protected int elementCount;
//容量增长系数:每次需要增长时,值增加一倍。
protected int capacityIncrement;
//序列化ID,保证序列化版本一致
private static final long serialVersionUID = -2767605614048989439L;
2、四个构造方法
//创建默认的Vector(默认初始容量为10,增长系数为0)
public Vector() {
//调用下面的构造方法
this(10);
}
//创建指定初始容量的Vector,增长系数为0
public Vector(int initialCapacity) {
//调用下面的构造方法
this(initialCapacity, 0);
}
//创建指定初始容量和增长系数的Vector
public Vector(int initialCapacity, int capacityIncrement) {
super();
//初始容量不能为0
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
//创建由指定Collection生成的Vector
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
//c.toArray有可能不会返回object[],所以下面增加一道保险
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
3、获取元素操作
//没啥好说,和ArrayList基本一致,就加上了个synchronized 保证同步
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
4、添加元素操作
//同步添加元素
public synchronized boolean add(E e) {
modCount++;
//每次添加都需要确认数组的容量大小(或有空间无操作,或是扩容)
ensureCapacityHelper(elementCount + 1);
//添加元素
elementData[elementCount++] = e;
return true;
}
//确保数组容量充足(minCapacity为可满足的最小容量)
private void ensureCapacityHelper(int minCapacity) {
//如果实际容量 < 可满足最小容量
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
//扩容操作(和ArrayList没差,就是扩容的大小有点区别)
private void grow(int minCapacity) {
//获取原容量
int oldCapacity = elementData.length;
//进行扩容:
//如果扩容系数不为0,则直接扩容此系数大小的空间
//如果扩容系数为0,则直接扩容原容量大小(即增加一倍)
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
//扩容后还是不满足最小容量要求
if (newCapacity - minCapacity < 0)
//就直接扩容到最小可满足容量
newCapacity = minCapacity;
//如果扩容后,大于数组的最大长度
if (newCapacity - MAX_ARRAY_SIZE > 0)
//调用此方法设置合适的容量大小
//若最小满足容量小于MAX_ARRAY_SIZE,则扩容到MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)
//若最小满足容量已经大于MAX_ARRAY_SIZE了,则扩容到Integer.MAX_VALUE(0x7fffffff)
newCapacity = hugeCapacity(minCapacity);
//最后将原数组中的数据复制到大小为 newCapacity 的新数组中,并将新数组赋值给 elementData。
elementData = Arrays.copyOf(elementData, newCapacity);
}
总结一下添加和扩容机制:
无论调用哪个方法进行元素添加,一定会先调用ensureCapacityHelper确保数组的容量足够在进行元素的添加。
在ensureCapacityHelper中判断是否需要扩容,若需要扩容,则调用grow进行扩容。
扩容时,先看容量增长系数CapacityIncrement是否为0
a)如果不为0,新容量 = 旧容量 + 增长系数
b)如果为0,新容量 = 旧容量 + 旧容量(即翻1倍)
若上面扩容后还不够,则新容量 = 传入的参数minCapacity(即可满足的最小容量)
再和MAX_ARRAY_SIZE比较(具体看上面源码注释),得到最终的扩容结果
最后后用Arrays.copyof()方法将元素拷贝到新的数组。
5、删除元素操作
//同样和ArrayList差不多,就只是加上synchronized
//不过Vector也提供了很多其他的删除方法:(功能看方法名就知道)
removeElementAt(int)、removeElement(Object)、removeAllElements()
6、其他方法
//设置Vector的元素个数
public synchronized void setSize(int newSize) {
//修改次数+1
modCount++;
//如果传进来的新size大小要大于当前的元素个数
if (newSize > elementCount) {
//则选择该值进行扩容操作
ensureCapacityHelper(newSize);
} else {
//否则,则将下标为[newSize,elementCount-1]的元素都设置为null
for (int i = newSize ; i < elementCount ; i++) {
elementData[i] = null;
}
}
elementCount = newSize;
}
//获取当前元素个数
public synchronized int size() {
return elementCount;
}
//获取当前数组容量
public synchronized int capacity() {
return elementData.length;
}
(三)线程安全性
是线程安全的(其实也只是相对安全,有些时候还是要加入同步语句来保证线程的安全),在增删改查方法上都加了synchronized修饰符,保证同步。
但是由于保证了线程安全,需要不断加锁,性能上也就相对较低。
注意:Vector中,加synchronized的方法都是public、protected方法(不是所有的public方法都加的),而像private这种仅被其他public方法调用的方法,并不需要加上synchronized
(四)克隆机制
浅克隆,同样比ArrayList多加了synchronized
(五)序列化机制
Vector中只有writeObject方法,没有readObject方法(所以序列化使用自定义的writeObject,反序列化使用默认的方法)
————————————————————————————————————————————————————————————
Stack
(一)概述
底层也是数组,继承了Vector类。(也仅仅直接继承Vector类,没有继承其他类,也没有直接实现其他接口)
(二)源码
//下面就是Stack类所有的源码
//各种方法其实都是调用Vector的相关方法,只不过对其封装成栈相关的方法
public class Stack<E> extends Vector<E> {
//创建一个空栈
public Stack() {
}
//入栈(不断在数组末尾加入,即栈顶是在数组末)
public E push(E item) {
addElement(item);
return item;
}
//出栈
public synchronized E pop() {
E obj;
//获得栈长度
int len = size();
obj = peek();
//根据下标出栈
removeElementAt(len - 1);
return obj;
}
//查看栈顶(数组最后一个元素)
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
//判空
public boolean empty() {
return size() == 0;
}
//从栈顶(数组末)开始查找,返回找到的第一个元素的下标
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
}
private static final long serialVersionUID = 1224463164541339165L;
}
栈的实现不仅仅只能通过数组来实现,也可以使用链表来实现