Java中List<E>對象賦值問題(引用傳遞和值傳遞)

Java中List<E>對象賦值操作問題

業務需求是:取2個集合中的交集對象並返回。如下代碼,busMap中key值和stocks中Map中的key值相等的對象則返回繼續操作,也就是說剔除stocks中的不存在於busMap中的對象,就是一個過濾操作。

實現代碼 ① bug版
報錯:
java.util.ConcurrentModificationException ; at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) ; at java.util.ArrayList$Itr.next(ArrayList.java:831) ;
原因:
forEach實現是採用Iterator實現的,而remove操作不能在Iterator下操作,所以報錯,具體原因是Iterator遍歷開始前,會對該對象生成一個改變前的標記值,在進行map.remove操作的時候,改變了這個值,導致循環結束後,程序檢查初始標記和結束標記不一致,就報錯了。而使用Iterator.remove操作時,它同時會修改這個初始標記值,一直讓這個初始標記值等於當前標記值,程序結束後判斷兩者相等,就不會有異常!

private static void  appendStock(Map<String,Object> busMap, List<Map<String, Object>> stocks , String key){
    if (stocks != null)
        for (Map<String,Object> s : stocks ) {
            boolean b = false;
            for (Map.Entry<String, Object> e : busMap.entrySet()) {
                if (s.get("stock_code") != null)
                    if (s.get("stock_code").toString().equals(e.getKey())) {
                        b = true;
                        break;
                    }
            }
            if ( !b ){
                stocks.remove(s);
            }
        }
}

代碼② 修正foreach中remove異常,bug版
bug:
執行該方法後,stocks並沒有改變!!
原因:
對象引用傳遞導致。這個stocks是形參,他會複製外界的引用,也就是說這個stocks和外界的stocks不是同一個引用,但是指向同一個內存地址。所以只要stocks指向的對象修改了,外界對象(同一個對象)也隨着修改。但是這個stocks是形參,是一個新的變量,生命週期只存在於該方法體內,所以代碼最後stocks = newStocks沒有任何意義,因爲調用方法結束,newStocks和stocks都會被GC幹掉,外界對象引用地址是沒有發生一點變化的。唯獨變化的就是外界對象的另一個引用(就是方法中的stocks)在方法中對其的修改!這就告訴我們一個道理:在設計方法是void還是return的時候,要看看外界引用是否需要被重新賦值。如果需要,則return,否則void。也就是void方法只是對外界對象的堆內存操作。return對象多了可以對外界對象引用的修改優勢,但是這要加大開銷!

private static void  appendStock(Map<String,Object> busMap, List<Map<String, Object>> stocks , String key){
    if (stocks != null)
        List<Map<String,Object>> newStocks =  Lists.newArrayList();  // 構建新對象保存交集數據
        for (Map<String,Object> s : stocks ) {
            for (Map.Entry<String, Object> e : busMap.entrySet()) {
                if (s.get("stock_code") != null)
                    if (s.get("stock_code").toString().equals(e.getKey())) {
                        newstocks.add(s);
                        break;
                    }
            }
        }
        stocks = newStocks;
}

代碼③ 繞開改變外界引用,穩定版
採用Iterator來刪除元素,摒棄拷貝,從而不入淺拷貝的坑

private static void  appendStock(Map<String,Object> busMap, List<Map<String, Object>> stocks , String key){
    if (stocks != null && stocks.size() > 0) {
        Iterator<Map<String, Object>> iterator = stocks.iterator();
        while (iterator.hasNext()) {
            Map<String, Object> s = iterator.next();
            if (s.get("stock_code") != null) {
                String stockCode = String.valueOf(s.get("stock_code"));
                if ( !busMap.containsKey(stockCode)) {
                    iterator.remove();
                }
            }
        }
    }
}

代碼④返回引用賦值給外界引用,穩定版

    private static List<Map<String, Object>>  appendStock(Map<String,Object> busMap, List<Map<String, Object>> stocks , String key){
    if (stocks != null)
        List<Map<String,Object>> newStocks =  Lists.newArrayList();  // 構建新對象保存交集數據
        for (Map<String,Object> s : stocks ) {
            for (Map.Entry<String, Object> e : busMap.entrySet()) {
                if (s.get("stock_code") != null)
                    if (s.get("stock_code").toString().equals(e.getKey())) {
                        newstocks.add(s);
                        break;
                    }
            }
        }
        return newStocks;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章