文章目录
- 栈和队列
- 一、Java中的Stack
- 1.Stack类的使用
- 2.Stack类的分析
- 二、Java中的Queue
- 1.Queue的使用
- 2.Queue的分析
- 2.1 add 和offer 区别:
- 2.2 element 和 peek 区别:
- 2.3 remove 和 poll 区别:
- 总结
栈和队列
栈和队列是最经常使用的数据结构之一。栈是一种先进后出,后进先出的线性表,队列是一种先进先出,后进后出的线性表。本篇文章主要讲述在Java中如何使用这两种数据结构。
一、Java中的Stack
1.Stack类的使用
Stack的声明如下,可以看到Stack继承了Vector,因此Stack可以使用Vector中的方法,如size() 等。
public
class Stack<E> extends Vector<E>
除此之外,Stack类定义了五个方法,作用如下
方法 | 作用 |
boolean empty() | 判断栈是否为空 |
E peek() | 返回栈顶部的对象,但不从栈中移除它 |
E pop() | 移除栈顶部的对象,并作为此函数的值返回该对象 |
E push(E item) | 把对象压入栈顶部 |
int search(Object o) | 返回对象在堆栈中的位置,若不存在则返回-1 |
示例:
Stack<Integer> stack = new Stack<>();
//1、2、3按顺序入栈
stack.push(1);
stack.push(2);
stack.push(3);
int a = stack.peek(); //返回栈顶元素3
int b = stack.pop(); //返回栈顶元素3,并将3出栈,此时栈中只剩2和1
int size = stack.size(); //获取栈的当前大小
boolean isEmpty = stack.empty(); //判断栈是否为空
int index = stack.search(1); //查找栈中是否有1,从栈顶开始计数,栈顶元素索引为1。此时从栈顶到栈底分别为2,1,该方法将返回2
2.Stack类的分析
先说说Stack的特点,上面说过Stack是继承自Vector的,而Vector是线程安全的,所以Stack也是线程安全的。下面分析Stack的三个关键方法push()、peek()和pop()。
public E push(E item) {
addElement(item);
return item;
}
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
从源码中我们可以看到上述三个方法的具体实现都是通过调用父类Vector里的方法实现的。为了实现线程安全,peek()和pop()加上了同步锁,push()没有添加是因为只调用了Vector中的addElement(E item)方法,这个方法是加锁的。
然而正因为Stack继承自Vector,Stack类已经不被官方推荐使用!!
基于 Vector 实现的栈 Stack。底层实际上还是数组,所以还是存在需要扩容。Vector 是由数组实现的集合类,它包含了大量集合处理的方法。而 Stack 之所以继承 Vector,是为了复用 Vector 中的方法,来实现进栈(push)、出栈(pop)等操作。这里就是 Stack 设计不好的地方,既然只是为了实现栈,不用链表来单独实现,而是为了复用简单的方法而迫使它继承 Vector,Stack 和 Vector 本来是毫无关系的。这使得 Stack 在基于数组实现上效率受影响,另外因为继承 Vector 类,Stack 可以复用 Vector 大量方法,这使得 Stack 在设计上不严谨。
官方推荐使用LinkedList来构建栈
二、Java中的Queue
1.Queue的使用
与Stack不同,Java里的Queue不是一个类,而是一个接口,它的声明为
public interface Queue<E> extends Collection<E>
其中声明了六个主要方法,具体如下
方法 | 作用 |
boolean add(E e) | 在队列尾部插入一个元素 |
boolean offer(E e) | 在队列尾部插入一个元素 |
E element() | 返回队列头部的对象,但不从栈中移除它 |
E peek() | 返回队列头部的对象,但不从栈中移除它 |
E remove() | 返回队列头部的对象,并从栈中移除它 |
E poll() | 返回队列头部的对象,并从栈中移除它 |
LinkedList实现了Queue接口,可以通过LinkedList来构建栈
2.Queue的分析
从上面的表我们发现,Queue中的六个方法,有三对方法的作用非常相似,分别为add和offer,element和peek,remove和poll。
2.1 add 和offer 区别:
看源码中add()和offer()的声明及注释
/**
* Inserts the specified element into this queue if it is possible to do so
* immediately without violating capacity restrictions, returning
* {@code true} upon success and throwing an {@code IllegalStateException}
* if no space is currently available.
*
* @param e the element to add
* @return {@code true} (as specified by {@link Collection#add})
* @throws IllegalStateException if the element cannot be added at this
* time due to capacity restrictions
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this queue
* @throws NullPointerException if the specified element is null and
* this queue does not permit null elements
* @throws IllegalArgumentException if some property of this element
* prevents it from being added to this queue
*/
boolean add(E e);
/**
* Inserts the specified element into this queue if it is possible to do
* so immediately without violating capacity restrictions.
* When using a capacity-restricted queue, this method is generally
* preferable to {@link #add}, which can fail to insert an element only
* by throwing an exception.
*
* @param e the element to add
* @return {@code true} if the element was added to this queue, else
* {@code false}
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this queue
* @throws NullPointerException if the specified element is null and
* this queue does not permit null elements
* @throws IllegalArgumentException if some property of this element
* prevents it from being added to this queue
*/
boolean offer(E e);
通过注释我们发现,add()和offer()向队列尾部中添加一个元素。他们的不同在于:当使用有容量限制的队列(如ArrayBlockingQueue)时,若队列已满,调用add会抛出一个IllegalStateException,而调用offer不会抛出异常,只会返回false。
2.2 element 和 peek 区别:
/**
* Retrieves, but does not remove, the head of this queue. This method
* differs from {@link #peek peek} only in that it throws an exception
* if this queue is empty.
*
* @return the head of this queue
* @throws NoSuchElementException if this queue is empty
*/
E element();
/**
* Retrieves, but does not remove, the head of this queue,
* or returns {@code null} if this queue is empty.
*
* @return the head of this queue, or {@code null} if this queue is empty
*/
E peek();
与add()和offer()类似,element() 和 peek()的区别在于:当队列为空时,调用element() 抛出一个NoSuchElementException 异常,而 peek() 返回 null。
2.3 remove 和 poll 区别:
/**
* Retrieves and removes the head of this queue. This method differs
* from {@link #poll() poll()} only in that it throws an exception if
* this queue is empty.
*
* @return the head of this queue
* @throws NoSuchElementException if this queue is empty
*/
E remove();
/**
* Retrieves and removes the head of this queue,
* or returns {@code null} if this queue is empty.
*
* @return the head of this queue, or {@code null} if this queue is empty
*/
E poll();
remove() 和 poll() 方法都是从队列中删除第一个元素。如果队列元素为空,调用remove() 的行为与 Collection 接口相似,会抛出NoSuchElementException 异常,而是新的 poll() 方法在用空集合调用时只是返回 null。
add、element、remove为一组,他们均在出错时抛出异常;offer、peek、poll为一组,他们在出错时返回特定的值
总结
以上内容分析了Java中的Stack和Queue,最关键的区别是Stack是一个类而Queue是一个接口。他们的共同点之一是都实现了Collection接口。他们都能通过LinkedList来实现,关于LinkedList将在另一篇文章中介绍。