List当中for循环的踩坑

来源:互联网 发布:费米估算法怎么用? 编辑:程序博客网 时间:2024/05/17 22:01

其实,本身我倒是没有踩坑,只是在做项目的时候留意了一下,然后等项目完成之后,自己又倒腾了一下,才发现,原来这里也有我的认知盲区,所以,把我这一次的经历分享一下。
关于List和for循环的知识点我就不做讲解了,我想万能的谷歌百度总能告诉你很多。
先贴一下我在工作中的代码吧

这里写图片描述

好吧,这里可能要说明一下,虽然我在这里用了平行流,当然这个不是重点,重点在于datalist是一个List,然后后面的forEach就是一个for循环,然后里面的getOverdueAndPenlty方法就是对datalist当中每一个元素进行赋值操作。
本身倒也没有什么问题,后来我突然想到,在for循环里面对其中的元素进行操作,这样是不是可以。当然,以我的经验,对对象当中的元素的修改,这样肯定是没有问题的,但是项目紧急,也来不及我多想什么,想着早点做完早点再去想想吧。


好吧,然后一拖拖到了三天……哈哈哈(^__^)


今天在整理材料的时候发现了当初的这个设想,然后,很装逼地给自己冲了一杯咖啡。无咖啡,不Java。
一开始自己做了一个小Demo

class Stu {    private int value;    public Stu(int v){        this.value = v;    }    public int getValue() {        return value;    }    public void setValue(int value) {        this.value = value;    }    @Override    public String toString() {        return "Stu [value=" + value + "]";    }}
public static void main(String[] args) {        List<Stu> list = new ArrayList<>();        list.add(new Stu(1));        list.add(new Stu(2));        list.add(new Stu(3));        for(Stu stu : list){            stu.setValue(stu.getValue()+1);        }        for(Stu s : list){            System.out.println(s);        }}

运行之后的结果:
这里写图片描述
看吧,说明对对象里面的值修改肯定是没什么问题的。那么我当时在考虑的是什么呢?
注意我说的话,对对象里面的值,而不是对象。
好吧,这话确实有点绕,那我再给你们演示一个Demo

public static void main(String[] args) {    List<Stu> list = new ArrayList<>();    list.add(new Stu(1));    list.add(new Stu(2));    list.add(new Stu(3));    for(Stu stu : list){        stu = new Stu(stu.getValue() + 1);    }    for(Stu s : list){        System.out.println(s);    }}

看上去好像也没什么不一样吧,但是结果还会一样吗?如图:
这里写图片描述
天哪‼️竟然没有变,我们不是明明修改了值吗?
好吧,这就是我当初感觉犹豫的地方。
那么就跟大家讲一下我的心路历程吧


我一开始考虑的时候,钻进了死胡同。我想的是,首先,list保存的是每一个变量的引用,也就是变量所处的堆栈的地址,然后这些堆栈各自指向堆当中的对象。如图:
这里写图片描述
好吧,这个手稿图画的还是蛮抽象的……(我自己都吐槽)
list当中保存的是stu1、stu2、stu3,然后各自对应着堆当中的1、2、3。其实这个图本身也没错,是这样的一回事情。然后我当时在想:

stu = new Stu(stu.getValue() + 1);

这个动作不就是让这个引用知道了其他的堆空间去了,list当中保存的栈依然没有变才是。但要是这样想,所以才钻了死胡同。
我甚至都在怀疑,上面这个赋值动作是否成功了,然后,就打印了一下,增加代码:

stu = new Stu(stu.getValue() + 1);System.out.println(stu);

结果:
这里写图片描述
对象确实改变了,这个是可以肯定的。
这个时候,我突然想到了一处地方。先不讲,我先把代码改动一下,给大家看看

    public static void main(String[] args) {        List<Stu> list = new ArrayList<>();        Stu stu1 = new Stu(1);        Stu stu2 = new Stu(2);        Stu stu3 = new Stu(3);        list.add(stu1);        list.add(stu2);        list.add(stu3);        for(Stu stu : list){            stu = new Stu(stu.getValue() + 1);            System.out.println(stu);        }        for(Stu s : list){            System.out.println(s);        }    }

大家有没有发现,我把添加到list里面的对象,单独拎出来,为什么这样做,是因为我想看看,for循环当中遍历的变量是不是同一个,Debug一下,如图:
这里写图片描述
这下恍然大悟,原来不是同一个(注意他们的id值,大家可以把它当做是地址)。
所以,for循环当中的引用其实只是一个copy。这样说可能大家不会太懂,我还是继续给大家画图吧:
这里写图片描述
好吧,不要再让我画图了……
给大家讲一下吧,就是在for循环当中,其实是对list当中的东西的一个copy,因为stu1指向的是堆空间1,然后stu=stu1,一开始都是指向对空间1,就是图片当中的①。然后我们把一个新的对象赋给了stu,然后stu指向了堆空间④,这个时候stu的value确实是4,所以我们去打印这个值也是4,但是for循环结束之后,我们发现,在list当中的依旧只是stu1、stu2、stu3,他们所指向的堆空间依然没有什么变化,所以还是1、2、3。而那些新增的堆空间,就找不到了,因为没有记录起来。然后这也可以解释,最早之前的程序

stu.setValue(stu.getValue()+1);

这个动作,确确实实是对堆空间1、2、3进行了修改。
想到这里,就觉得一切都豁然开朗了,是的吧。


好吧,虽然只是开发过程中的一个小插曲,但是细细想来还是蛮有意思的,我觉得我可以把我的经历分享给大家,带给大家学习,当然,也希望可以的话,能打赏一些,这样我就有钱去买咖啡喝啦,哈哈哈……
这里写图片描述

原创粉丝点击