接下来将继续用Java实现线性结构中另一个比较经典的结构-栈。

目录

定义

基本算法

栈在Java中的地位

Java中堆与栈

Java中在堆与栈中如何存放数据

栈在Java中存储

代码实现(Java)

创建一个栈

压入元素

取出元素

查看栈顶元素

判断栈是否为空

主方法

运行结果

总结

栈和堆的特点


栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。

定义

栈是限定仅在表头进行插入和删除操作的线性表。

基本算法

1.进栈(PUSH)算法

①若TOP≥n时,则给出溢出信息,作出错处理(进栈前首先检查栈是否已满,满则溢出;不满则作②);

②置TOP=TOP+1(栈指针加1,指向进栈地址);

③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;
    }

执行步骤分为:

  1. main()函数是程序入口,JVM先执行,在栈内存中开辟一个空间,存放int类型变量x,同时附值1。
  2. JVM执行show()函数,在栈内存中又开辟一个新的空间,存放int类型变量x,同时附值2。此时main空间与show空间并存,同时运行,互不影响。
  3. show()执行完毕,变量x立即释放,空间消失。但是main()函数空间仍存在,main中的变量x仍然存在,不受影响。

Java栈是怎么实现的 java栈的方法_Java栈是怎么实现的

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

Java栈是怎么实现的 java栈的方法_堆栈区_02

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会启动垃圾回收机制,不定时自动删除。

Java栈是怎么实现的 java栈的方法_数据结构_03

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仍然指向,所以数组不消失。

Java栈是怎么实现的 java栈的方法_Java栈是怎么实现的_04

public void car(){
        Car c=new Car;
        c.color="blue";
        Car c1=new Car;
        c1.num=5;
    }

 虽然是个对象都引用new Car,但是是两个不同的对象。每一次new,都产生不同的实体

Java栈是怎么实现的 java栈的方法_堆栈区_05

public void carV() {
        Car c = new Car;
        c.num = 5;
        Car c1 = c;
        c1.color = "green";
        c.run();
    }

Car c1=c,这句话相当于将对象复制一份出来,两个对象的内存地址值一样。所以指向同一个实体,对c1的属性修改,相当于c的属性也改了。

Java栈是怎么实现的 java栈的方法_Java栈_06

代码实现(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());
    }

运行结果

Java栈是怎么实现的 java栈的方法_算法_07

Java栈是怎么实现的 java栈的方法_Java栈_08

总结

栈和堆的特点

函数中定义的基本类型变量,对象的引用变量都在函数的栈内存中分配。
栈内存特点,数数据一执行完毕,变量会立即释放,节约内存空间。
栈内存中的数据,没有默认初始化值,需要手动设置。

堆内存用来存放new创建的对象和数组。
堆内存中所有的实体都有内存地址值。
堆内存中的实体是用来封装数据的,这些数据都有默认初始化值。
堆内存中的实体不再被指向时,JVM启动垃圾回收机制,自动清除,这也是JAVA优于C++的表现之一(C++中需要程序员手动清除)。

注:

什么是局部变量:定义在方法(函数)中的变量、定义在方法(函数)中的参数上的变量、定义在for循环内部的变量

参考文献:

百度百科: