LeetCode刷題時引發(fā)的思考:Java中ArrayList存放的是值還是引用惜傲?

前言

今天我在刷LeetCode的時候遇到了一個問題洽故,就是ArrayList添加不進去數(shù)據(jù),其實不是沒有添加進去盗誊,而是添加進去的數(shù)據(jù)被改變了时甚,為什么會改變了呢隘弊?其實涉及到ArrayList存放的是值還是引用的問題,網(wǎng)上有很多回答是:如果是基本數(shù)據(jù)類型則存放的是值荒适,如果是對象存放的就是引用梨熙。那么到底是什么呢,讓我們來一探究竟吧刀诬!

正文

原題在此:

39. 組合總和

給定一個無重復元素的數(shù)組 candidates 和一個目標數(shù) target 咽扇,找出 candidates 中所有可以使數(shù)字和為 target 的組合。

candidates 中的數(shù)字可以無限制重復被選取舅列。

我一開始寫的代碼是這樣的:

class Solution {

    private List<List<Integer>> result = new ArrayList();
    private ArrayList<Integer> solution = new ArrayList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backtracking(candidates,target,0);
        return result;
    }

    public  void backtracking(int[] candidates, int residual, int start) {
        if (residual < 0) {
            return;
        }

        if (residual == 0) {
            result.add(solution);
            return;
        }
        for(int i = start;i<candidates.length;i++) {
            solution.add(candidates[i]);
            backtracking(candidates,residual-candidates[i],i);
            solution.remove(solution.size()-1);
        }
    }

}

看了一遍肌割,沒什么問題,非常自信的點了運行帐要,結果懵逼了把敞,List里面什么也沒有

遇Bug沒關系,首先來分析一下:既然輸出是兩個 []榨惠,那么List中肯定添加過兩次數(shù)據(jù)奋早,也就是進入了if語句中,那么可能的原因就是代碼寫的有問題導致 solution中沒有數(shù)據(jù)赠橙,那就打印一下看看唄耽装。

修改代碼:


    ……
        if (residual == 0) {
            System.out.println(solution.toString());    //打印solution
            result.add(solution);
            return;
        }
    ……

看一下控制臺,solution正常打印了期揪,而且結果是正確的

既然solution有正確的數(shù)據(jù)掉奄,那么問題就應該出現(xiàn)在List的添加上,那就再打印一下看看凤薛。

修改代碼:


    ……
        if (residual == 0) {
            result.add(solution);
            System.out.println("----------");
            for (List<Integer> integers : result) {
                System.out.println(integers.toString());    //打印List中的內容
            }
            System.out.println("==========");
            return;
        }
    ……

從打印的內容上看姓建,第一次添加的是 [2,2,3],沒有問題缤苫。但是第二次的打印結果就很奇怪速兔,是兩個 [7],也就是我第一次添加的內容被修改了活玲。那么可能的原因只有一個涣狗,List中添加進去的是一個引用,而并非實際的值舒憾,不然結果怎么會改變呢镀钓。但是List中存放的真的是引用嗎?我們來進一步的驗證珍剑。

首先我們將ArrayList中的泛型指定為基本數(shù)據(jù)類型的包裝類和我們自定義的一個User類:

public class ArrayListTest {

    static class User {
        private String name;
        private int age;

        public User(String name , int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    public static void main(String[] args) {
        ArrayList<Integer> integerArrayList = new ArrayList<>();
        int testInt = 10;
        integerArrayList.add(testInt);
        testInt = 20;
        integerArrayList.add(testInt);
        System.out.println(integerArrayList.toString());

        ArrayList<Double> doubleArrayList = new ArrayList<>();
        double testDouble = 100.0;
        doubleArrayList.add(testDouble);
        testDouble = 200.0;
        doubleArrayList.add(testDouble);
        System.out.println(doubleArrayList.toString());

        ArrayList<User> userArrayList = new ArrayList<>();
        User testUser = new User("張三",20);
        userArrayList.add(testUser);
        testUser.age = 22;
        userArrayList.add(testUser);
        for (User user : userArrayList) {
            System.out.println(user.toString());
        }
    }

這里我們試了Integer和Double兩種掸宛,看一下結果:

由此可見,修改int和double不會對之前的內容造成影響招拙,但是修改User會對之前的內容造成影響唧瘾。所以措译,ArrayList中如果是基本數(shù)據(jù)類型,那么存放的就是值饰序,如果是對象领虹,那么存放的就是對象的引用而不是對象的拷貝∏笤ィ看樣子這個結論是正確的塌衰,但是需要注意的一個問題就是ArrayList可以存放基本數(shù)據(jù)類型嗎?如果將泛型指定為基本數(shù)據(jù)類型就會報錯:

所以說蝠嘉,ArrayList不存在存放的是基本數(shù)據(jù)類型的問題最疆,只能存放基本數(shù)據(jù)類型的包裝類,也就是說存放基本數(shù)據(jù)類型的時候會自動裝箱成一個對象蚤告,然后把引用存放進去努酸。那好,就算基本數(shù)據(jù)類型存不了杜恰,存的是包裝類获诈,那么我修改了里面的內容之后,Integer和Double兩次卻存放了不同的值心褐,而User修改后存放了兩個相同的內容舔涎。因為如果泛型是Integer或Double的話,兩次存放的是不同的對象的引用而不是一個逗爹,如果是一個的話亡嫌,那當然會導致之前的內容改變咯【蚨《Java編程思想》里面說過:無論何時昼伴,對同一個對象調用hashCode()都應該生成同樣的值。所以我們來打印一下他們的hash值就可以判斷是不是同一個對象镣屹。我們使用System.identityHashCode()來獲取hash值:

    ……
    public static void main(String[] args) {
        ArrayList<Integer> integerArrayList = new ArrayList<>();
        int testInt = 10;
        integerArrayList.add(testInt);
        testInt = 20;
        integerArrayList.add(testInt);
        for (Integer integer : integerArrayList) {
            System.out.println(integer + " : "+ System.identityHashCode(integer));
        }
        System.out.println();

        ArrayList<Double> doubleArrayList = new ArrayList<>();
        double testDouble = 100.0;
        doubleArrayList.add(testDouble);
        testDouble = 200.0;
        doubleArrayList.add(testDouble);
        for (Double aDouble : doubleArrayList) {
            System.out.println(aDouble + " : "+ System.identityHashCode(aDouble));
        }

        ArrayList<User> userArrayList = new ArrayList<>();
        User testUser = new User("張三",20);
        System.out.println("\nUser修改前:");
        System.out.println(testUser.toString());
        System.out.println(System.identityHashCode(testUser));
        userArrayList.add(testUser);
        testUser.age = 22;
        System.out.println("User修改后:");
        System.out.println(testUser.toString());
        System.out.println(System.identityHashCode(testUser));
        userArrayList.add(testUser);
        System.out.println("\n遍歷ArrayList<User>:");
        for (User user : userArrayList) {
            System.out.println(user.toString());
            System.out.println(System.identityHashCode(user));
        }

    }
    ……

打印的結果如下:

從打印的結果可以看出,Integer和Double并不是修改內容价涝,而是存了一個新的對象的引用進去女蜈,所以存放基本數(shù)據(jù)類型的包裝類也是引用并非是值,而User對象修改后卻可以影響到之前已經(jīng)存儲的內容色瘩,兩個User是同一個伪窖。

為什么Integer和Double修改不了呢?因為他們都屬于不可變量居兆,都是final修飾的覆山,也就是說第二次賦值的時候指向的是一個新的對象,而不是修改之前的內容泥栖。從源碼中我們可以看到:

可以看到簇宽,基本數(shù)據(jù)類型的包裝類和String一樣都是final修飾的勋篓,而且Double和Integer等基本數(shù)據(jù)類型包裝類中也沒有提供修改值的方法,也就是說之前看樣子是在修改數(shù)據(jù)魏割,其實是指向了一個新的內存地址譬嚣,ArrayList中第二次存放數(shù)據(jù)的時候,并沒有改變第一次存放的引用中的內存地址中的值钞它,而是存了一個新的引用拜银。

結論

那么到現(xiàn)在為止,就可以得出一個結論了: ArrayList中存放的是引用而不是值遭垛。

網(wǎng)上有的人說“存基本數(shù)據(jù)類型存的是值尼桶,存對象存的是引用”這個結論是錯誤的【庖牵回到最開始的問題泵督,我這題應該怎么寫?其實只要克隆一份就好了:


    ……
        if (residual == 0) {
            result.add((List<Integer>) solution.clone());
            return;
        }
    ……

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末卵酪,一起剝皮案震驚了整個濱河市幌蚊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌溃卡,老刑警劉巖溢豆,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瘸羡,居然都是意外死亡漩仙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門犹赖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來队他,“玉大人,你說我怎么就攤上這事峻村◆镎郏” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵粘昨,是天一觀的道長垢啼。 經(jīng)常有香客問我,道長张肾,這世上最難降的妖魔是什么芭析? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮吞瞪,結果婚禮上馁启,老公的妹妹穿的比我還像新娘。我一直安慰自己芍秆,他們只是感情好惯疙,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布翠勉。 她就那樣靜靜地躺著,像睡著了一般螟碎。 火紅的嫁衣襯著肌膚如雪眉菱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天掉分,我揣著相機與錄音俭缓,去河邊找鬼。 笑死酥郭,一個胖子當著我的面吹牛华坦,可吹牛的內容都是我干的。 我是一名探鬼主播不从,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惜姐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了椿息?” 一聲冷哼從身側響起歹袁,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寝优,沒想到半個月后条舔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡乏矾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年孟抗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钻心。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡凄硼,死狀恐怖,靈堂內的尸體忽然破棺而出捷沸,到底是詐尸還是另有隱情摊沉,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布痒给,位于F島的核電站坯钦,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一匀钧、第九天 我趴在偏房一處隱蔽的房頂上張望梅鹦。 院中可真熱鬧,春花似錦鲁豪、人聲如沸潘悼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽治唤。三九已至棒动,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宾添,已是汗流浹背船惨。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缕陕,地道東北人粱锐。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像扛邑,于是被迫代替她去往敵國和親怜浅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354