Stack与栈(Java语言)
前言
通过前面的对于ArrayList的学习,我们知道了ArrayList底层就是一个数组,而数组存储数据,我们也可以换一种说法,叫做顺序表。而这篇博客我们就可以系统的介绍一下Stack,也就是栈这种数据结构。
前景介绍
在介绍Stack之前,我们先引入几个问题:
什么是栈?
简而言之,栈是一种数据结构,它的特点就是:先进后出。
举个例子:假如有一组数据12,23,34,45。
我们将数据放入栈中。
可以看到我们是按照12,23,34,45的顺序放入栈中的,那么拿出数据时,就需要先拿出45,34,23,最后拿出12这个数据,所以说叫做先进后出。
什么是Java虚拟机栈?
我们知道JVM内存结构分为五部分:Java虚拟机栈,本地方法栈,堆,方法区,程序计数器。所以我们可以说Java虚拟机栈是JVM当中的一块内存,该内存一般用来存放局部变量等,既然是栈,那么同样它具有栈的特点:先进后出。
什么是栈帧?
这边先挖一个坑,关于栈帧,我们会专门写一篇博客介绍栈帧,我们需要简单知道,当调用函数的时候,我们会专门为这个函数调用开辟一块内存,叫做栈帧。当一个函数结束,那么该函数的内存释放。
Java内置栈
对于Java而言,它是有内置的栈直接供我们使用的。也就是Stack类。
栈对象
跟ArrayList一样,通过new创建对象。
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
}
栈方法
对于栈本身而言,栈自己有push()方法,pop()方法,peek()方法,empty()方法,search()方法。
push()方法
形式:public E push(E item)
解释:在栈的最顶部放入一个元素item。
例子:
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
}
这时栈中依次放入1,2,3,4。
pop()方法
形式:public synchronized E pop()
解释:移除栈顶的元素,并返回该元素。
例子:
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack.pop());//弹出栈顶元素,并且删除 4
}
结果:
peek()方法
形式:public synchronized E peek()
解释:返回栈顶元素,但并不删除。
例子:
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack.pop());//弹出栈顶元素,并且删除 4
System.out.println(stack.peek());//获取栈顶元素,但不删除 3
System.out.println(stack.peek());//获取栈顶元素,但不删除 3
}
结果:
empty()方法
形式:public boolean empty()
解释:判断栈是否为null
例子:
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack.pop());//弹出栈顶元素,并且删除 4
System.out.println(stack.peek());//获取栈顶元素,但不删除 3
System.out.println(stack.peek());//获取栈顶元素,但不删除 3
System.out.println(stack.empty());//栈中还有1,2,3
}
结果:
search()方法
形式:public synchronized int search(Object o)
解释:如果栈中存在o这个元素,那么返回该元素离栈顶的距离,如果不存在则返回-1。
例子:
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack.pop());//弹出栈顶元素,并且删除 4
System.out.println(stack.peek());//获取栈顶元素,但不删除 3
System.out.println(stack.peek());//获取栈顶元素,但不删除 3
System.out.println(stack.empty());
System.out.println(stack.search(3));
}
结果:
实现栈
为了能够更好的学习栈,我们通过代码自己实现栈。
成员变量和构造方法
public class MyStack {
public int[] elem;
public int usedSize;
public MyStack() {
this.elem = new int[5];
}
}
对于栈来说,底层可以用一个数组实现,因此我们需要一个数组存储元素,同样需要一个usedSize作为计数。构造方法只需要实例化一个5个元素的数组,如果不够扩容即可。
push()方法
public boolean isFull() {
return this.usedSize == this.elem.length;
}
public void push(int val) {
if (isFull()) {
//扩容
this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
}
this.elem[this.usedSize] = val;
this.usedSize++;
}
在增加元素进入栈时,需要判断栈是否为满,如果为满,那么按照两倍进行扩容,如果没有满,那么在usedSize的位置放入val的值,并且usedSize++。
pop()方法
public boolean isEmpty() {
return this.usedSize == 0;
}
public int pop() {
if (isEmpty()) {
throw new RuntimeException("栈为空!");
}
int oldVal = this.elem[usedSize - 1];
this.usedSize--;
return oldVal;
}
首先判断栈是否为null,如果栈为null,则无法弹出栈顶元素,因此抛出异常,否则,将数组中usedSize-1的位置的元素返回,并且usedSize–。
peek()方法
public int peek() {
if (isEmpty()) {
throw new RuntimeException("栈为空!");
}
return elem[usedSize - 1];
}
与pop()方法类似,只是不需要usedSize-1即可。
search()方法
public int search(int val) {
int count = usedSize - 1;
while (elem[count] != val) {
count--;
if (count<0){
return -1;
}
}
return usedSize-count;
}
将usedSize-1的值赋值给count,当count下标对应的元素不与val值相等,那么count–,直到相等或者count<0,如果相等,返回usedSize与count的差值,如果count<0返回-1。
关于栈我们就介绍到这里,栈的使用核心就是了解到它是先进后出的特点就行了,当然想要更快的掌握栈离不开刷题,如果有什么问题,或者建议,欢迎私信或者评论。谢谢各位!