天天看點

List當中for循環的踩坑

其實,本身我倒是沒有踩坑,隻是在做項目的時候留意了一下,然後等項目完成之後,自己又倒騰了一下,才發現,原來這裡也有我的認知盲區,是以,把我這一次的經曆分享一下。

關于List和for循環的知識點我就不做講解了,我想萬能的谷歌百度總能告訴你很多。

先貼一下我在工作中的代碼吧

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());
        list.add(new Stu());
        list.add(new Stu());
        for(Stu stu : list){
            stu.setValue(stu.getValue()+);
        }
        for(Stu s : list){
            System.out.println(s);
        }
}
           

運作之後的結果:

List當中for循環的踩坑

看吧,說明對對象裡面的值修改肯定是沒什麼問題的。那麼我當時在考慮的是什麼呢?

注意我說的話,對對象裡面的值,而不是對象。

好吧,這話确實有點繞,那我再給你們示範一個Demo

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

看上去好像也沒什麼不一樣吧,但是結果還會一樣嗎?如圖:

List當中for循環的踩坑

天哪‼️竟然沒有變,我們不是明明修改了值嗎?

好吧,這就是我當初感覺猶豫的地方。

那麼就跟大家講一下我的心路曆程吧

我一開始考慮的時候,鑽進了死胡同。我想的是,首先,list儲存的是每一個變量的引用,也就是變量所處的堆棧的位址,然後這些堆棧各自指向堆當中的對象。如圖:

List當中for循環的踩坑

好吧,這個手稿圖畫的還是蠻抽象的……(我自己都吐槽)

list當中儲存的是stu1、stu2、stu3,然後各自對應着堆當中的1、2、3。其實這個圖本身也沒錯,是這樣的一回事情。然後我當時在想:

這個動作不就是讓這個引用知道了其他的堆空間去了,list當中儲存的棧依然沒有變才是。但要是這樣想,是以才鑽了死胡同。

我甚至都在懷疑,上面這個指派動作是否成功了,然後,就列印了一下,增加代碼:

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

結果:

List當中for循環的踩坑

對象确實改變了,這個是可以肯定的。

這個時候,我突然想到了一處地方。先不講,我先把代碼改動一下,給大家看看

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

大家有沒有發現,我把添加到list裡面的對象,單獨拎出來,為什麼這樣做,是因為我想看看,for循環當中周遊的變量是不是同一個,Debug一下,如圖:

List當中for循環的踩坑

這下恍然大悟,原來不是同一個(注意他們的id值,大家可以把它當做是位址)。

是以,for循環當中的引用其實隻是一個copy。這樣說可能大家不會太懂,我還是繼續給大家畫圖吧:

List當中for循環的踩坑

好吧,不要再讓我畫圖了……

給大家講一下吧,就是在for循環當中,其實是對list當中的東西的一個copy,因為stu1指向的是堆空間1,然後stu=stu1,一開始都是指向對空間1,就是圖檔當中的①。然後我們把一個新的對象賦給了stu,然後stu指向了堆空間④,這個時候stu的value确實是4,是以我們去列印這個值也是4,但是for循環結束之後,我們發現,在list當中的依舊隻是stu1、stu2、stu3,他們所指向的堆空間依然沒有什麼變化,是以還是1、2、3。而那些新增的堆空間,就找不到了,因為沒有記錄起來。然後這也可以解釋,最早之前的程式

這個動作,确确實實是對堆空間1、2、3進行了修改。

想到這裡,就覺得一切都豁然開朗了,是的吧。

好吧,雖然隻是開發過程中的一個小插曲,但是細細想來還是蠻有意思的,我覺得我可以把我的經曆分享給大家,帶給大家學習,當然,也希望可以的話,能打賞一些,這樣我就有錢去買咖啡喝啦,哈哈哈……

List當中for循環的踩坑