深入理解java的堆栈

背景:在这里插入图片描述
如图,有两个链表,t和s。现在,想在t链表的末尾插入s。
迭代的写法大家都不陌生:

public static void main(String[] args) {
    ListNode t = new ListNode(1, new ListNode(2, null));
    ListNode s = t;
    ListNode e = new ListNode(3, null);
    // 区别
    while (t.next != null) {
        t = t.next;
    }
    t.next = e;
    System.out.println(s);
} 

那么,程序运行时,jvm的堆栈是怎么变化的?

之所以有这个疑问,是因为我写的时候,偶然用了第一种方法写,发现没有奏效,就去想堆栈了


// 无法改值
public static void main(String[] args) {
        ListNode t = new ListNode(1, new ListNode(2, null));
        ListNode s = t;
        ListNode e = new ListNode(3, null);
        // 区别
        while (t != null) {
            t = t.next;
        }
        t= e;
        System.out.println(s);
    } 

// 可以改变值
public static void main(String[] args) {
    ListNode t = new ListNode(1, new ListNode(2, null));
    ListNode s = t;
    ListNode e = new ListNode(3, null);
    // 区别
    while (t.next != null) {
        t = t.next;
    }
    t.next = e;
    System.out.println(s);
} 

两段代码,不同点就在于,循环体,第一代代码,是判断当前的节点是否为空,不为空就遍历到下一节点。第二段代码,判断当前的节点的下一节是否为空,不为空则遍历到下一节点。

看起来,好像差不多,但实际,如果运行,打印出来,第一段,s打印出来的是1–>2; 第二段,s打印出来的是1–>2–>3;

那么为什么会出现这样的情况呢?就是对java的变量引用和堆栈的理解不够深;

简单抽象成以下:jvm里面有堆和栈;堆里存储实体对象,栈里存放变量和值
在这里插入图片描述
具体可以这么理解,比如:ListNode a=new ListNode(1,null);在jvm就是这样:
在这里插入图片描述
具体解读:
a存在栈中,new出来的具体对象存在堆中;a实际存的是地址,地址指向这个对象。对象里的val和next同理;注意,这里,next变量是null,不占用堆和栈。 在这里插入图片描述

搞懂这个之后,我们再看这串代码:


// 无法改值
public static void main(String[] args) {
        ListNode t = new ListNode(1, new ListNode(2, null));
        ListNode s = t;
        ListNode e = new ListNode(3, null);
        // 区别
        while (t != null) {
            t = t.next;
        }
        t= e;
        System.out.println(s);
    } 

// 可以改变值
public static void main(String[] args) {
    ListNode t = new ListNode(1, new ListNode(2, null));
    ListNode s = t;
    ListNode e = new ListNode(3, null);
    // 区别
    while (t.next != null) {
        t = t.next;
    }
    t.next = e;
    System.out.println(s);
} 

在这里插入图片描述
首先,将在堆里的对象,我们抽象为节点。

s的值和t的值一样,都是节点1的地址值,可以理解为都指向节点1;

t.next就是节点2的地址值;

因此t=t.next,找到了节点2的地址值,并且赋给当前变量t,以此来完成指针移动。

那么,两端代码的差异到底是什么呢?

第一段代码中,t=null的前一次迭代情况是什么呢?是遍历2节点的时候:在这里插入图片描述

也就是t等于2节点的地址值,t.next=null;s保存的还是节点1的地址值;

当我们继续遍历:t=t.next ,也就是t=null在这里插入图片描述当我们运行t=e,e就是节点3的位置:你会发现s的关系没有变,还是原来的,s=address_a,addreaa_a.next=address_2;

在这里插入图片描述
那么如果是第二段代码呢?
在这里插入图片描述 回到这里:遍历到2节点的时候。t.next=null为true,那么,就会跳出循环,执行t.next=e;
也就是,栈中会出现一个next变量,存的是节点3的地址值。

这里就不画图了:上图的绿色就是此时的t,每有执行t.next=e的时候,t.next=null,因此栈中暂时不会有next这个变量,因为null变量不占用堆栈。当执行t.next=e的时候,next变量才入栈,且为节点3的地址值;

由此:s存的是节点1的地址值,节点1中的next存的是节点2的地址值,节点2的next存的是节点3的地址值;

总结:
第一段代码: t=null时候,s和原来的链表是一样的。最后一个节点的next指向null,在堆栈中都不占用位置。之所以会产生让这个时候的t指向新的节点,能改变s的莫阶段,是因为,把这个时候最后一个节点的next存在了栈中,所以,你以为,让next指向新的节点,应该是能成功的。但其实,next=null的时候,next在堆栈中都不占用位置。当你的t指向null的时候,原来的链表next是不占用位置der。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值