介绍
栈是一种基于后进先出(LIFO)策略的集合类型。在应用程序中使用栈迭代器的一个典型原因是在用集合保存元素的同时颠倒它们的顺序。栈的一个典型用例:把标准输入的所有整数逆序排列。
1 | public class StackOfType { |
API
泛型可迭代的栈API:
public class Stack |
|
---|---|
Stack() | 创建一个空栈 |
void push(Item item) | 添加一个元素 |
Item pop() | 删除最近添加的元素 |
boolean isEmpty() | 栈是否为空 |
int size() | 栈中元素数量 |
Dijkstra的双栈算术表达式求值算法
求表达式:(1 + (( 2 + 3 ) ( 4 5 )))。用两个栈,一个用于保存运算符,一个用于保存操作数实现。
1 | public class Evaluate{ |
表达式由括号、运算符和操作数组成,所以上面代码的处理过程是:
- 将操作数压入操作数栈;
- 将运算符压入运算符栈;
- 忽略左括号;
- 在遇到右括号时,弹出一个运算符弹出所需数量的操作数并将运算符和操作数的运算结果压入操作数栈。
定容栈
一种表示容量固定的的字符串栈的抽象数据类型。该定容栈的实现:
1 | public class FixedCapacityStackOfString{ |
该栈的第一个缺点是只能处理String对象,而java不允许创建泛型数组,为了解决这个问题,下面实现一个泛型栈
1 | public class FixedCapacityStack<Item>{ |
选择用数组表示栈内容,因为数组一旦创建,其大小是无法改变的,所以栈使用的空间只能是这个数组大小的一部分。为了能够使栈保存所有元素,又不浪费空间,我们需要一个能动态调整栈大小,并支持迭代的栈。
动态调整大小的栈实现
能够动态调整数组大小,并支持迭代的栈的实现:
1 | import java.util.Iterator; |
这份泛型的可迭代的Stack API的实现是所有集合类抽象数据类型实现的模板,它将所有元素保存在数组中,支持按照后进先出的顺序迭代访问所有栈元素,并动态调整数组的大小以保持数组大小和栈的大小之比小于一个常数。该实现达到了任意集合类数据类型的实现的最佳性能:
- 每项操作的用时都与集合大小无关;
- 空间需求总是不超过集合大小乘以一个常数
- 缺点是push()和pop()操作可能会调整数组的大小,这项操作的耗时和栈的大小成正比。
对象游离
java的垃圾收集策略是回收所有无法被访问的对象的内存。在上面对pop()的实现中,被弹出的元素的引用仍然存在于数组中,这个元素实际上已经不会再被访问了,但java的垃圾收集器没法知道这一点,除非改引用被覆盖。这种保存一个不需要的对象的引用称为游离。
避免游离只需将被弹出的数组元素的值设为null,这将覆盖无用的引用并使系统可以在用例使用完被弹出的元素后回收它的内存。
迭代
1 | Iterator<String> i = collection.iterator(); |
这段代码展示了任意可迭代的集合数据类型中需要实现的东西:
- 集合数据类型必须实现一个iterator()方法并返回一个iterator对象;
- Iterstor类必须包含两个方法:hasNext()和next()。
要是一个类可迭代,第一步就是在它的声明中加入:implements Iterable
1 | public interface Iterable<Item>{ |
然后在类中添加一个方法iterator()并返回一个迭代器Iterator
1 | public Iterator<Item> iterator(){ |
迭代器是一个实现了hasNext()和next()方法的类的对象,由Iterator接口定义:
1 | public interface Iterator<Item>{ |
对于上面的迭代器,它的实现在栈的一个嵌套类中:
1 | private class ReverseArrayIterator implements Iterator<Item>{ |