P132-P142 栈,数组实现
Implementing Collections
定长栈(fixed-capacity stack)
public class FixedCapacityStackOfStrings
{
private String[] a; // stack entries
private int N; // size
public FixedCapacityStackOfStrings(int cap)
{ a = new String[cap]; } //构造函数,需要指定长度,不支持iteration
public boolean isEmpty() { return N == 0; }
public int size() { return N; }
public void push(String item)
{ a[N++] = item; }
public String pop()
{ return a[--N]; }
}
开发一个API的实现最主要的就是选择一种数据结构
使用数组实现非常简单,
- 所有的item都保持在他们插入的位置
- N=0的时候栈为空
- 栈顶 在 a[N-1] 的地方
优点:push 和 pop 与数组的大小无关
缺点
- 泛型:之前的这个只能使用 String,现在我们需要使用泛型来提高它的通用性
public class FixedCapacityStack<Item>
{
private Item[] a; // stack entries
private int N; // size
public FixedCapacityStack(int cap)
{ a = (Item[]) new Object[cap]; } //历史遗留问题,不能直接新建一个 new Item[]
public boolean isEmpty() { return N == 0; }
public int size() { return N; }
public void push(Item item)
{ a[N++] = item; }
public Item pop()
{ return a[--N]; }
}
- 数组大小调整
选择数组来表示stack,暗示客户端必须估计好最大的栈大小。我们不能改变一个数组的长度。
public void resize(int N) {
Item[] resizedItems = (Item[]) new Object[N];
for (int i = 0; i < a.length; i++) {
resizedItems[i] = a[i]; //copy the data from member a to resizedItem
}
a = resizedItems; //link the resized Items to a;
}
这段代码不难理解,把a里面的元素复制给一个临时变量。然后把临时变量的引用再还给a。
- Iteration
迭代
例子:使用 foreach 来遍历一个集合
Stack<String> collection = new Stack<String>();
...
for (String s : collection)
StdOut.println(s);
...
例子:使用迭代器,应该可以取得与foreach相同的效果
Iterator<String> i = collection.iterator();
while (i.hasNext())
{
String s = i.next();
StdOut.println(s);
}
要使用迭代器:
我们必须要有两个条件:
1. 必须实现了, iterable 接口,能够返回一个 Iterator 对象
public interface Iterable<Item>
{
Iterator<Item> iterator(); //自己创造的集合类,需要实现这个接口,返回一个Iterator对象
}
2. Iterator 对象必须有 hasNext, next() 实现了 Iterator 接口
public Iterator<Item> iterator()
{ return new ReverseArrayIterator(); } //stack必须要反向输出
public interface Iterator<Item> // 所有的迭代器需要实现这个接口
{
boolean hasNext();
Item next();
void remove();
}
实现了 Iterable 接口的好处,我们可以使用foreach而不需要关心数据结构的内部实现。这可以让我们改变实现的类型,而不需要改变我们客户端的代码。
例子, collection a 和 collection b全部都实现了 Iterable 接口,客户端的代码都是遍历返回的集合。当我们的实现从A 换到 B 的时候我们不需要改变客户端的for each。因为他们都实现了Iterable接口。
Algorithm 1.1
import java.util.Iterator;
public class ResizingArrayStack<Item> implements Iterable<Item> {
private Item[] a = (Item[]) new Object[1]; // stack items
private int N = 0; // number of items
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
private void resize(int max) { // Move stack to a new array of size max.
Item[] temp = (Item[]) new Object[max];
for (int i = 0; i < N; i++)
temp[i] = a[i];
a = temp;
}
public void push(Item item) { // Add item to top of stack.
if (N == a.length) resize(2 * a.length);
a[N++] = item;
}
public Item pop() { // Remove item from top of stack.
Item item = a[--N]; //书上的例子应该有一些问题,这里我们应该先需要判断是不是可以pop?
a[N] = null; // Avoid loitering (see text).
if (N > 0 && N == a.length / 4) resize(a.length / 2);
return item;
}
public Iterator<Item> iterator() {
return new ReverseArrayIterator();
}
private class ReverseArrayIterator implements Iterator<Item> { // Support LIFO iteration.
private int i = N;
public boolean hasNext() {
return i > 0;
}
public Item next() {
return a[--i];
}
public void remove() {
}
}
}