接下来将继续用Java实现线性结构中另一个比较经典的结构-栈。
目录
栈
定义
基本算法
栈在Java中的地位
Java中堆与栈
Java中在堆与栈中如何存放数据
栈在Java中存储
代码实现(Java)
创建一个栈
压入元素
取出元素
查看栈顶元素
判断栈是否为空
主方法
运行结果
总结
栈和堆的特点
栈
堆
栈
栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。
定义
栈是限定仅在表头进行插入和删除操作的线性表。
基本算法
1.进栈(PUSH)算法
①若TOP≥n时,则给出溢出信息,作出错处理(进栈前首先检查栈是否已满,满则溢出;不满则作②);
③S(TOP)=X,结束(X为新进栈的元素);
2.退栈(POP)算法
①若TOP≤0,则给出下溢信息,作出错处理(退栈前先检查是否已为空栈, 空则下溢;不空则作②);
②X=S(TOP),(退栈后的元素赋给X):
③TOP=TOP-1,结束(栈指针减1,指向栈顶)。
栈在Java中的地位
JAVA在程序运行时,在内存中划分5片空间进行数据的存储。
分别是:1:寄存器、2:本地方法区、3:方法区、4:栈、5:堆。
关于Java中其他的数据存储结构,请移步专栏:数据结构与算法分析Java全解析
Java中堆与栈
Java中在堆与栈中如何存放数据
基本数据类型、局部变量都是存放在栈内存中的,用完就消失。
new创建的实例化对象及数组,是存放在堆内存中的,用完之后靠垃圾回收机制不定期自动消除。
栈在Java中存储
public static void main(String[] args) {
int x = 1;
show();
}
private static void show() {
int x = 2;
}
执行步骤分为:
- main()函数是程序入口,JVM先执行,在栈内存中开辟一个空间,存放int类型变量x,同时附值1。
- JVM执行show()函数,在栈内存中又开辟一个新的空间,存放int类型变量x,同时附值2。此时main空间与show空间并存,同时运行,互不影响。
- show()执行完毕,变量x立即释放,空间消失。但是main()函数空间仍存在,main中的变量x仍然存在,不受影响。
public void arr() {
int[] x = new int[3];
x[0] = 20;
}
执行步骤分为:
执行int[] x=new int[3]
JVM执行arr()函数,在栈内存中开辟一个空间,存放x变量(x变量是局部变量)。
同时,在堆内存中也开辟一个空间,存放new int[3]数组,堆内存会自动内存首地址值,如0x0045。
数组在栈内存中的地址值,会附给x,这样x也有地址值。所以,x就指向(引用)了这个数组。此时,所有元素均未附值,但都有默认初始化值0。
执行x[0]=20
即在堆内存中将20附给[0]这个数组元素。这样,数组的三个元素值分别为20,0,0
public void arrNull() {
int[] x = new int[3];
x[0] = 20;
x = null;
}
执行步骤分为:
执行int[] x=new int[3]
JVM执行arr()函数,在栈内存中开辟一个空间,存放x变量(x变量是局部变量)。
同时,在堆内存中也开辟一个空间,存放new int[3]数组,堆内存会自动内存首地址值,如0x0045。
数组在栈内存中的地址值,会附给x,这样x也有地址值。所以,x就指向(引用)了这个数组。此时,所有元素均未附值,但都有默认初始化值0。
执行x[0]=20
即在堆内存中将20附给[0]这个数组元素。这样,数组的三个元素值分别为20,0,0
执行x=null;
null表示空值,即x的引用数组内存地址0x0045被删除了,则不再指向栈内存中的数组。此时,堆中的数组不再被x使用了,即被视为垃圾,JVM会启动垃圾回收机制,不定时自动删除。
public void arrNew() {
int[] x = new int[3];
int[] y = x;
y[1] = 100;
x = null;
}
执行步骤分为:
执行int[] x=new int[3]
JVM执行arr()函数,在栈内存中开辟一个空间,存放x变量(x变量是局部变量)。
同时,在堆内存中也开辟一个空间,存放new int[3]数组,堆内存会自动内存首地址值,如0x0045。
数组在栈内存中的地址值,会附给x,这样x也有地址值。所以,x就指向(引用)了这个数组。此时,所有元素均未附值,但都有默认初始化值0。
执行int[] y=x
在栈内存定义了新的数组变量内存y,同时将x的值0x0045附给了y。所以,y也指向了堆内存中的同一个数组。
执行y[1]=100
即在堆内存中将20附给[0]这个数组元素。这样,数组的三个元素值分别为0,100,0
执行x=null
则变量x不再指向栈内存中的数组了。但是,变量y仍然指向,所以数组不消失。
public void car(){
Car c=new Car;
c.color="blue";
Car c1=new Car;
c1.num=5;
}
虽然是个对象都引用new Car,但是是两个不同的对象。每一次new,都产生不同的实体
public void carV() {
Car c = new Car;
c.num = 5;
Car c1 = c;
c1.color = "green";
c.run();
}
Car c1=c,这句话相当于将对象复制一份出来,两个对象的内存地址值一样。所以指向同一个实体,对c1的属性修改,相当于c的属性也改了。
代码实现(Java)
栈的基本操作包括创建栈、入栈、出栈、获取栈顶元素、获取栈的大小、清空栈、销毁栈。
以下仅适用数组模拟一个栈结构
创建一个栈
package stack;
/**
* 实现栈的基本操作
*
* @author Soinice
* @date 2019/4/25 22:38
*/
public class MyStack {
/**
* 栈的底层我们使用数组来存储数据
*/
int[] numArr;
public MyStack() {
numArr = new int[0];
}
}
压入元素
/**
* 压入元素
*
* @param num
* @return void
* @author Soinice
* @date 2019/4/25 22:44
*/
public void push(int num) {
// 创建一个新的数组
int[] newArr = new int[numArr.length + 1];
// 把原数组中的元素复制到新数组中
for (int i = 0; i < numArr.length; i++) {
newArr[i] = numArr[i];
}
// 把添加的元素放入新数组中
newArr[numArr.length] = num;
// 使用新数组替换旧数组
numArr = newArr;
}
取出元素
/**
* 取出栈顶元素
*
* @param
* @return int
* @author Soinice
* @date 2019/4/25 22:54
*/
public int pop() {
// 栈中没有元素
if (numArr.length == 0) {
throw new RuntimeException("stack is empty");
}
// 取出数组中的最后一个元素
int lastNum = numArr[numArr.length - 1];
// 创建一个新的数组
int[] newArr = new int[numArr.length - 1];
// 原数组中除了最后一个元素的其他元素都放入新数组中
for (int i = 0; i < numArr.length - 1; i++) {
newArr[i] = numArr[i];
}
// 替换数组
numArr = newArr;
// 返回栈顶元素
return lastNum;
}
查看栈顶元素
/**
* 查看栈顶元素
*
* @param
* @return int
* @author Soinice
* @date 2019/4/25 23:07
*/
public int peek() {
// 栈中没有元素
if (numArr.length == 0) {
throw new RuntimeException("stack is empty");
}
return numArr[numArr.length - 1];
}
判断栈是否为空
/**
* 判断栈是否为空
*
* @param
* @return boolean
* @author Soinice
* @date 2019/4/25 23:12
*/
public boolean isEmpty() {
return numArr.length == 0;
}
主方法
/**
* 实现栈的基本操作
*
* @param args
* @return void
* @author Soinice
* @date 2019/4/25 23:15
*/
public static void main(String[] args) {
// 创建一个栈
MyStack myStack = new MyStack();
// 压入数组
myStack.push(9);
myStack.push(8);
myStack.push(7);
// 取出栈顶元素
System.out.println("//--------------------------取出栈顶元素");
System.out.println("myStack.pop() = " + myStack.pop());
System.out.println("myStack.pop() = " + myStack.pop());
System.out.println("myStack.pop() = " + myStack.pop());
// 查看栈顶元素
System.out.println("//----------------查看栈顶元素");
System.out.println(myStack.peek());
// 判断栈是否为空
System.out.println(myStack.isEmpty());
}
运行结果
总结
栈和堆的特点
栈
函数中定义的基本类型变量,对象的引用变量都在函数的栈内存中分配。
栈内存特点,数数据一执行完毕,变量会立即释放,节约内存空间。
栈内存中的数据,没有默认初始化值,需要手动设置。
堆
堆内存用来存放new创建的对象和数组。
堆内存中所有的实体都有内存地址值。
堆内存中的实体是用来封装数据的,这些数据都有默认初始化值。
堆内存中的实体不再被指向时,JVM启动垃圾回收机制,自动清除,这也是JAVA优于C++的表现之一(C++中需要程序员手动清除)。
注:
什么是局部变量:定义在方法(函数)中的变量、定义在方法(函数)中的参数上的变量、定义在for循环内部的变量
参考文献:
百度百科:栈