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;
}