Java数据结构 - 栈
一、什么是栈
栈属于线性表的一种特殊结构,栈中的元素被限制为只能从表的一端进出,这一端称之为栈顶,另一端称为栈底,元素进出的顺序遵循先进后出(First In Last Out),或后进先出(Last In First Out)。
压栈(push):将元素插入表中的操作叫做进栈/压栈/入栈,插入元素在栈顶。
出栈(pop):将元素从表中删除的操作叫出栈/弹栈,删除元素在栈顶。
二、模拟实现容器类MyStack
1. 栈的顺序存储结构
import java.util.Arrays;
/**
* 模拟实现栈容器类 ——— 使用数组存储结构的方式
*/
public class MyStack<E> {
//使用数组来存储栈元素
private Object[] elementData;
//默认栈容量大小为10
private static final int DEFAULT_CAPACITY = 10;
//实际栈容量大小
private int size = 0;
/**
* 创建一个默认初始容量大小的栈对象
*/
public MyStack(){
this.elementData = new Object[DEFAULT_CAPACITY];
}
/**
* 创建一个指定初始容量的栈对象
* @param initialCapacity 指定初始容量
*/
public MyStack(int initialCapacity){
this.elementData = new Object[initialCapacity];
}
/**
* 将元素e入栈并返回
* @param e 元素对象
* @return e
*/
public E push(E e){
ensureCapacity();
elementData[size++] = e;
return e;
}
//检测当前栈容量大小,如果已满则进行扩容,扩容大小为原数组长度的2倍
private void ensureCapacity(){
if(size == elementData.length){
int oldLen = elementData.length;;
//手动扩容
// Object[] newElementData = new Object[oldLen * 2];
// for(int i = 0; i < oldLen; i++){
// newElementData[i] = elementData[i];
// }
// elementData = newElementData;
//使用Arrays.copyOf()
elementData = Arrays.copyOf(elementData,oldLen * 2);
}
}
/**
* 将当前栈顶的元素出栈并返回,如果栈为空,抛出异常
* @return 栈顶元素
*/
@SuppressWarnings("unchecked")
public E pop(){
if(isEmpty()){
throw new EmptyStackException("当前栈为空!");
}else{
return (E) elementData[--size];
}
}
/**
* @return 当前栈是否为空
*/
public boolean isEmpty(){
return size == 0;
}
/**
* 返回当前栈顶的元素,如果栈为空,抛出异常
* @return 栈顶元素
*/
@SuppressWarnings("unchecked")
public E peek(){
if(isEmpty()){
throw new EmptyStackException("当前栈为空!");
}else{
return (E)elementData[size-1];
}
}
/**
* @return 返回当前栈的元素个数
*/
public int size(){
return size;
}
}
2. 栈的链式存储结构
/**
* 模拟实现栈容器类 ——— 使用单链表存储结构的方式
*/
public class MyStack2<E> {
//内部结点类 - 存放栈的元素
private static class Node<E>{
E item;
Node<E> next;
Node(E element){
this.item = element;
}
Node(E element,Node<E> next){
this.item = element;
this.next = next;
}
}
private Node<E> head; //记录链表头结点
private int size; //栈中元素个数
//创建一个栈对象
public MyStack2(){
}
/**
* 将元素e入栈并返回
* @param element 元素对象
* @return e
*/
public E push(E element){
//使用头插法
Node<E> newNode = new Node<>(element);
newNode.next = head;
head = newNode;
size++;
return element;
}
/**
* 将栈顶元素出栈并返回,如果栈为空,则抛出异常
* @return 栈顶元素
*/
public E pop(){
if(isEmpty()){
throw new EmptyStackException("当前栈为空!");
}else{
E popped = head.item;
head = head.next;
size--;
return popped;
}
}
/**
* 就栈顶元素返回,如果栈为空,则抛出异常
* @return 栈顶元素
*/
public E peek(){
if(isEmpty()){
throw new EmptyStackException("当前栈为空!");
}else{
return head.item;
}
}
/**
* @return 返回当前栈中的元素个数
*/
public int size(){
return size;
}
/**
* @return 当前栈是否为空
*/
public boolean isEmpty(){
return head == null;
}
}
三、Java集合框架中的Stack类
-
Java官方提供的Stack类是继承于Vector容器类的,Vector是最早的对象容器类,于ArrayList相比是线程安全的,但是效率低。
-
查阅源码可以看到Stack类中并没有字段属性,而是使用的继承于Vector的elementData。且Stack类中的成员方法也是在Vector提供的方法上进行了封装。
四、概念区分
栈、虚拟机栈、栈帧有什么区别呢?
栈:栈是数据结构中一种特殊的线性表,栈中元素只能从栈顶进出,元素遵循先进后出的原则。
虚拟机栈:是JVM的一块内存空间
栈帧:是在调用方法的过程中,在Java虚拟机栈上开辟的一块内存。
文章为本人独立编写,难免会有错误之处。
如发现有误,恳请评论提出!