問題由來
曾經遇到過一條面試題,“Java中的Stack是通過Vector來實現的,這種設計被認為是不良的設計,說說你的看法?”
解析Java中的Stack
眾所周知Stack(棧)是一種先進后出的數據結構。當中有兩個重要的方法:push(進棧)和pop(出棧)。
幾乎所有語言在實現棧時,都會實現這兩個方法,進棧和出棧。而棧這種數據結構在多數時候用來插入和刪除元素(進棧則是在頂部插入元素,出棧則是從頂部刪除元素),較少情況會用來查找元素。所以從實現方式上,大多是以鏈表方式實現而非數值方式實現(在插入刪除方法上,鏈表效率優於數組效率)。
反觀Java中的Stack,查看源代碼:
1: public class Stack<E> extends Vector<E> {
2: /**
3: * Creates an empty Stack.
4: */
5: public Stack() {
6: }
7:
8: /**
9: * Pushes an item onto the top of this stack. This has exactly
10: * the same effect as:
11: * <blockquote><pre>
12: * addElement(item)</pre></blockquote>
13: *
14: * @param item the item to be pushed onto this stack.
15: * @return the <code>item</code> argument.
16: * @see java.util.Vector#addElement
17: */
18: public E push(E item) {
19: addElement(item);
20:
21: return item;
22: }
23:
24: /**
25: * Removes the object at the top of this stack and returns that
26: * object as the value of this function.
27: *
28: * @return The object at the top of this stack (the last item
29: * of the <tt>Vector</tt> object).
30: * @exception EmptyStackException if this stack is empty.
31: */
32: public synchronized E pop() {
33: E obj;
34: int len = size();
35:
36: obj = peek();
37: removeElementAt(len - 1);
38:
39: return obj;
40: }
41:
42: /**
43: * Looks at the object at the top of this stack without removing it
44: * from the stack.
45: *
46: * @return the object at the top of this stack (the last item
47: * of the <tt>Vector</tt> object).
48: * @exception EmptyStackException if this stack is empty.
49: */
50: public synchronized E peek() {
51: int len = size();
52:
53: if (len == 0)
54: throw new EmptyStackException();
55: return elementAt(len - 1);
56: }
57:
58: /**
59: * Tests if this stack is empty.
60: *
61: * @return <code>true</code> if and only if this stack contains
62: * no items; <code>false</code> otherwise.
63: */
64: public boolean empty() {
65: return size() == 0;
66: }
67:
68: /**
69: * Returns the 1-based position where an object is on this stack.
70: * If the object <tt>o</tt> occurs as an item in this stack, this
71: * method returns the distance from the top of the stack of the
72: * occurrence nearest the top of the stack; the topmost item on the
73: * stack is considered to be at distance <tt>1</tt>. The <tt>equals</tt>
74: * method is used to compare <tt>o</tt> to the
75: * items in this stack.
76: *
77: * @param o the desired object.
78: * @return the 1-based position from the top of the stack where
79: * the object is located; the return value <code>-1</code>
80: * indicates that the object is not on the stack.
81: */
82: public synchronized int search(Object o) {
83: int i = lastIndexOf(o);
84:
85: if (i >= 0) {
86: return size() - i;
87: }
88: return -1;
89: }
90:
91: /** use serialVersionUID from JDK 1.0.2 for interoperability */
92: private static final long serialVersionUID = 1224463164541339165L;
可以看到Stack繼承Vector,而Vector是由數組實現的集合類,他包含了大量集合處理的方法。而Stack之所以繼承Vector,是為了復用Vector中的方法,來實現進棧(push)、出棧(pop)等操作。
這里就是質疑Stack的地方,既然只是為了實現棧,為什么不用鏈表來單獨實現,只是為了復用簡單的方法而迫使它繼承Vector(Stack和Vector本來是毫無關系的)。這使得Stack在基於數組實現上效率受影響,另外因為繼承Vector類,Stack可以復用Vector大量方法,這使得Stack在設計上不嚴謹,當我們看到Vector中的:
1: public void add(int index, E element) {
2: insertElementAt(element, index);
3: }
可以在指定位置添加元素,這與Stack 的設計理念相沖突(棧只能在棧頂添加或刪除元素)。
所以Java中的Stack實現確實值得商榷。
使用鏈表模擬Stack
一個單純的棧,其實可以只包含以下方法:
boolean empty()
測試堆棧是否為空。
T peek()
查看堆棧頂部的對象,但不從堆棧中移除它。
T pop()
移除堆棧頂部的對象,並作為此函數的值返回該對象。
void push(T item)
把項壓入堆棧頂部。
我們使用鏈表來模擬一個棧:
1: public class Stack<T> {
2: private Node<T> top;
3:
4: /**
5: * 返回棧頂元素
6: *
7: * @return
8: */
9: public T peek() {
10: return top.getData();
11: }
12:
13: /**
14: * 從棧中彈出項
15: * @return
16: */
17: public T pop() {
18: T data = null;
19: if (top != null) {
20: data = top.getData();
21: top = top.getNextNode();
22: }
23: return data;
24: }
25:
26: /**
27: * 向棧內壓入項
28: *
29: * @param data
30: */
31: public void push(T data) {
32: Node<T> node = new Node<T>();
33: node.setData(data);
34: if (top == null) {
35: top = node;
36: } else {
37: node.setNextNode(top);
38: top.setPreNode(node);
39: top = node;
40: }
41: }
42:
43: /**
44: * 是否是空棧
45: *
46: * @return
47: */
48: public boolean isEmpty() {
49: return top == null;
50: }
51: }
1: public class Node<T> {
2: private T data;
3: // 前驅節點
4: private Node<T> preNode;
5: // 后繼節點
6: private Node<T> nextNode;
7:
8: public T getData() {
9: return data;
10: }
11:
12: public void setData(T data) {
13: this.data = data;
14: }
15:
16: public Node<T> getPreNode() {
17: return preNode;
18: }
19:
20: public void setPreNode(Node<T> preNode) {
21: this.preNode = preNode;
22: }
23:
24: public Node<T> getNextNode() {
25: return nextNode;
26: }
27:
28: public void setNextNode(Node<T> nextNode) {
29: this.nextNode = nextNode;
30: }
31:
32: }
編寫一個main測試:
1: /**
2: * @param args
3: */
4: public static void main(String[] args) {
5: Stack<Integer> stack = new Stack<Integer>();
6: stack.push(1);
7: stack.push(2);
8: System.out.println(stack.peek());
9: stack.push(3);
10: stack.push(4);
11: System.out.println(stack.peek());
12: stack.push(5);
13: stack.push(6);
14: System.out.println(stack.pop());
15: System.out.println(stack.peek());
16: }