1、栈初识
Stack类是jdk中java.util包下的一个类。其方法有
栈是一种用于存储数据的简单数据结构,与普通的线性表不同,线性表是先进先出,即FIFO,但是栈是先进后出,即FILO。我们一般只能对栈顶元素进行操作。
2、Stack类的继承关系接口实现
Stack继承了Vector类,Vector类又实现了List接口。
3、Stack类的特点
3.1 构造函数
从源码中了解到,Stack类只有一个无参构造函数
/**
* Creates an empty Stack.
*/
public Stack() {
}
所以,我们无法像ArrayList或者HashMap那样,初始化指定栈的容量大小。那么栈的初识化容量是多少呢?什么时候进行扩容呢?扩容的默认容量是多少呢?
由于Stack继承了Vector类,在Vector类的源码中可以看出:
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);
}
public Vector() {
this(10);
}
可以看到,如果我们不指定vector的容量大小,那么初始化的默认容量就是10。这和ArrayList的初始化容量一致。
那么何时扩容呢?Stack的push()方法如下
public E push(E item) {
addElement(item);
return item;
}
Stack直接调用了Vector类的addElement(E item)方法
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
可以看到,是在计算插入数据后容量大小与原来容量大小做比较,除非栈满了,才会进行扩容。扩容调用的就是grow(minCapacity)
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);
}
可以看到,如果没有指定每次扩容的容量大小,默认的新容量将会是两倍的旧容量。
这一点与我们熟知的ArrayList的扩容大小不一样,ArrayList的新容量大小是旧容量加上旧容量的值的值右移一位。即
n
e
w
C
a
p
a
c
i
t
y
=
o
l
d
C
a
p
a
c
i
t
y
+
(
o
l
d
C
a
p
a
c
i
t
y
>
>
1
)
newCapacity=oldCapacity+(oldCapacity>>1)
newCapacity=oldCapacity+(oldCapacity>>1)
3.2 pop与peek
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);
}
这两个方法都加锁了,是线程安全的操作。
pop操作当中包含了peek操作,pop是返回栈顶元素,并将该元素从栈中删除。而peek只返回栈顶元素,不删除。
栈的底层数据结构用的是数组。
4、应用
栈的一些应用主要就是运用其先进后出的特性,比较典型的应用就是表达式计算,包括中缀表达式、后缀表达式以及符号匹配等等。
先留个坑,有空再填。