【Java】集合(List、Set、Map)遍歷、刪除、比較元素時的小陷阱
主要說明List,其餘兩個都一樣
一、漏網之魚-for迴圈遞增下標方式遍歷集合,並刪除元素
如果你用for迴圈遞增下標方式遍歷集合,在遍歷過程中刪除元素,你可能會遺漏了某些元素。說那麼說可能也說不清楚,看以下示例:
import java.util.ArrayList;import java.util.List; public class ListTest_Unwork { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); System.out.println("Original list : " + list); String temp = null; for (int i = 0; i < list.size(); i++) { temp = list.get(i);
System.out.println("Check for " + temp); if ("3".equals(temp)) { list.remove(temp); } } System.out.println("Removed list : " + list); } }
日誌列印:
Original list : [1, 2, 3, 4, 5]Check for 1 Check for 2 Check for 3 Check for 5 Removed list : [1, 2, 4, 5]
如日誌所見,其中值為4的元素並未經過判斷,漏網之魚。
解決方法為以下兩個(但一般不建議我們在遍歷中用不是遍歷本身的函式刪除元素,見關於“ConcurrentModificationException”的內容):
1、對於此情況,我一般都從後面開始遍歷,以避免問題:
import java.util.ArrayList;import java.util.List; public class ListTest_Work { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); System.out.println("Original list : " + list); System.out.println(); String temp = null; for (int i = list.size() - 1; i >= 0; i--) { temp = list.get(i); System.out.println("Check for " + temp); if ("3".equals(temp)) { list.remove(temp); } } System.out.println("Removed list : " + list); } }
2、直接從新建立一個集合,重新擺放,但消耗記憶體,慎用:
import java.util.ArrayList;import java.util.List; public class ListTest_Work2 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); System.out.println("Original list : " + list); System.out.println(); List<String> tempList = new ArrayList<String>(); for (String temp : list) { System.out.println("Check for " + temp); if (!"3".equals(temp)) { tempList.add(temp); } } System.out.println("Removed list : " + tempList); } }
二、ConcurrentModificationException異常-Iterator遍歷集合過程中用其他手段(或其他執行緒)操作元素
ConcurrentModificationException是Java集合的一個快速報錯(fail-fast)機制,防止多個執行緒同時修改同一個集合的元素。在用Iterator遍歷集合時,如果你用其他手段(非Iterator自身手段)操作集合元素,就會報ConcurrentModificationException。
不信?用Iterator方式或簡寫的for(Object o : list) {}方式,遍歷集合,修改元素時會報異常:
import java.util.ArrayList;import java.util.List; public class ListTest2_Unwork { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); System.out.println("Original list : " + list); System.out.println(); for (String temp : list) { System.out.println("Check for " + temp); if ("3".equals(temp)) { list.remove(temp); } } System.out.println("Removed list : " + list); } }
或
import java.util.ArrayList;import java.util.Iterator; import java.util.List; public class ListTest3_Unwork { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); System.out.println("Original list : " + list); System.out.println(); Iterator<String> i = list.iterator(); String temp = null; while (i.hasNext()) { temp = i.next(); System.out.println("Check for " + temp); if ("3".equals(temp)) { list.remove(temp); } } System.out.println("Removed list : " + list); } }
日誌:
Original list : [1, 2, 3, 4, 5]Check for 1 Check for 2 Check for 3 Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) at java.util.ArrayList$Itr.next(ArrayList.java:831) at ListTest3_Unwork.main(ListTest3_Unwork.java:20)
在刪除元素“3”時,會報異常。
對於此情況,需要用iterator的remove方法替代,結果是妥妥的:
import java.util.ArrayList;import java.util.Iterator; import java.util.List; public class ListTest3_Work { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); System.out.println("Original list : " + list); System.out.println(); Iterator<String> i = list.iterator(); String temp = null; while (i.hasNext()) { temp = i.next(); System.out.println("Check for " + temp); if ("3".equals(temp)) { i.remove(); } } System.out.println("Removed list : " + list); } }
延伸個小問題,為什麼for(Object o : list) {}方式遍歷集合,現象和Iterator方式一樣,都會報錯呢?
答:這是因為Java的糖語法,“for(Object o : list) {}方式”只是Java語言用“易用性糖衣”吸引你的手段,本質上,它也是Iterator。不信,你寫下下面這段程式,反編譯看看就清楚了:
import java.util.ArrayList;import java.util.List; public class ForTester { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("a"); for (String s : list) { list.remove(s); System.out.println(s); } } }
反編譯
import java.util.ArrayList; import java.util.Iterator; public class ForTester { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("a"); Iterator arg2 = list.iterator(); while (arg2.hasNext()) { String s = (String) arg2.next(); list.remove(s); System.out.println(s); } } }
三、Set集合操作陷阱iew plain co
- package com.sort;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.Set;
- /**
- * 一個不包含重複元素的 collection。更確切地講,set 不包含滿足 e1.equals(e2) 的元素對 e1 和 e2
- */
- publicclass SetTest2 {
- publicstaticvoid main(String[] args) {
- Set<String> set = new HashSet<String>();
- set.add("a");
- set.add("b");
- set.add("c");
- set.add("d");
- set.add("e");
- /**
- * 遍歷方法一,迭代遍歷
- */
- for(Iterator<String> iterator = set.iterator();iterator.hasNext();){
- System.out.print(iterator.next()+" ");
- }
- System.out.println();
- System.out.println("********************");
- /**
- * for增強迴圈遍歷
- */
- for(String value : set){
- System.out.print(value+" ");
- }
- }
- }
當然Set集合也可以使用for迴圈了。
注意:這裡Set集合中放入的是String型別,假如我們放入一個自己定義的類例項的時候,比如Person類例項,這時候我們要自己重寫hashcode和equal方法,因為當使用HashSet時,會先比較物件的hashCode(),如果儲存在集合中的物件的hash
code值是否與增加的物件的hash code值一致;如果不一致,直接加進去;如果一致,再進行equals方法的比較,equals方法如果返回true,表示物件已經加進去了,就不會再增加新的物件,否則加進去。
下面分析一下Set集合的另外一個重要實現類TreeSet
TreeSet使用元素的自然順序對元素進行排序,或者根據建立 set 時提供的 Comparator 進行排序,具體取決於使用的構造方法。
通俗一點講,就是可以按照排序後的列表顯示,也可以按照指定的規則排序view
plain cop
- Set<String> set = new TreeSet<String>();
- set.add("f");
- set.add("a");
- set.add("b");
- set.add("c");
- set.add("d");
- set.add("e");
- System.out.println(set);
輸出:[a, b, c, d, e, f]
按照排序後輸出。那麼如果我們想讓他倒序輸出呢?當然方法很多。這裡我採用指定一個規則讓他倒序輸出
- import java.util.Comparator;
-
相關推薦
【Java】集合(List、Set、Map)遍歷、刪除、比較元素時的小陷阱
主要說明List,其餘兩個都一樣 一、漏網之魚-for迴圈遞增下標方式遍歷集合,並刪除元素 如果你用for迴圈遞增下標方式遍歷集合,在遍歷過程中刪除元素,你可能會遺漏了某些元素。說那麼說可能也說不清楚,看以下示例: import ja
Spring中集合(List,Set,Map)的配置和簡單使用(一)
1、首先寫一個實體類 package com.listtest.test; import java.util.List; import java.util.Map; import java.util.Set; public class Collect {
Java集合整理(List and Set and Map)
集合的由來 陣列長度是固定,當新增的元素超過了陣列的長度時需要對陣列重新定義,太麻煩,java內部給我們提供了集合類,能儲存任意物件,長度是可以改變的,隨著元素的增加而增加,隨著元素的減少而減少 陣列和集合的區別 區別1 : 陣列既可以儲存基本資
List、set以及map的遍歷方式的整理
@Test public void foreachSet() { Set setObj = new HashSet(); setObj.add("dsf"); setObj.add(1); setObj.add(5); //first
Java中的集合(List和Set)
Java容器類主要是為了“儲存物件”,並將其劃分為兩個不同的概念:Collection,獨立元素的集合,這些元素都服從一條或多條規則,如List必須按照插入順序儲存元素,Set不能有重複元素,Queue按照排隊規則來確定物件的順序。Map形成一組“鍵值對”物件,允許你使用鍵
【Java】集合系列18(Arrays和Collections工具類)
1、Arrays類 Arrays類常用方法概述: 本類所有方法都是靜態的,本類方法是針對陣列的操作。 //部分Arrays的靜態方法(JDK1.8) static <T> List<T> asList(T... a)
【Java】 集合系列01(總體框架)
Java集合是Java提供的工具包,包含了常用的資料結構:集合、連結串列、佇列、棧、陣列、對映等。 Java集合工具包的位置是Java.util.*。 Java集合主要可以劃分為四個部分:List列表、Set集合、Map對映、工具類(Iterator迭代器、
【java】簡介(一)
編碼 http ... 設計 適合 不能 高度 代碼格式 操作系統 應用:web後端開發、android-app開發、大數據應用開發 學習:java會過時,但程序設計的思想不會過時 特點:1、面向對象,跨平臺,語法比c++簡單 2、以字節碼的形式運行在虛擬機上
【Java】DateUtil(2)
繼承 ava sim pla bool private throw ons tar import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat;
【java】集合學習——Map 之 LinkedHashMap
前言 jdk 版本 jdk1.8.0_161 UML結構圖 LinkedHashMap:Map 介面的 雜湊表 和 連結列表的 實現。 相對於 HashMap 的特性是:有序性(插入元素的順序有序),因為內部使用了 雙向連結串列實現。 原始碼 建構函式 主要
【java】json和list互相轉化工具類
import java.util.List; import net.sf.json.JSONArray; public class JsonListUtil { /** * Li
【BZOJ1443】遊戲(二分圖匹配,博弈論)
() ans evel getchar mes 最大匹配 開始 就會 明顯 【BZOJ1443】遊戲(二分圖匹配,博弈論) 題面 BZOJ 題解 很明顯的二分圖博弈問題。 發現每次移動一定是從一個黑點到達一個白點,或者反過來。 所以可以對於棋盤進行染色然後連邊。 考慮一下必
【BZOJ3712】Fiolki(並查集重構樹)
bzoj3 long std 不同 合並 ++i 卡爾 優先級 href 【BZOJ3712】Fiolki(並查集重構樹) 題面 BZOJ 題解 很神仙的題目。 我們發現所有的合並關系構成了一棵樹。 那麽兩種不同的東西如果產生反應,一定在兩個聯通塊恰好聯通的時候反應。 那麽
【BZOJ4455】小星星(動態規劃,容斥)
之間 lld algorithm std 還需要 tchar 一次 lin 還需 【BZOJ4455】小星星(動態規劃,容斥) 題面 BZOJ 洛谷 Uoj 題解 題意說簡單點就是給定一張\(n\)個點的圖和一棵\(n\)個點的樹,現在要讓圖和樹之間的點一一對應,並且如果樹
【HDU2604】Queuing(矩陣快速冪+遞推)
題目連結 Queuing Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(
【POJ3169】Layout(差分約束系統+SPFA)
題目連結 Layout Time Limit: 1000MS Memory Limit: 65536K Total Submissions:14919 Accepted: 7183 Description Like everyone
#290. 【ZJOI2017】仙人掌(數數+仙人掌+樹形dp)
傳送門 模擬賽的時候打了個表發現為一條鏈的時候答案是\(2^{n-2}\)竟然順便過了第一個點 然後之後訂正的時候強聯通分量打錯了調了一個上午 首先不難發現我們可以去掉所有在環上的邊,那麼就變成了一個森林,不同的樹之間不可能有連邊,那麼只要所有樹的答案乘起來就好了,只要在每一棵樹內部樹形\(dp\)即可
【BZOJ2330】【SDOI2012】糖果(差分約束,SPFA)
【BZOJ2330】【SDOI2012】糖果 題面 題目描述 幼兒園裡有N個小朋友,lxhgww老師現在想要給這些小朋友們分配糖果,要求每個小朋友都要分到糖果。但是小朋友們也有嫉妒心,總是會提出一些要求,比如小明不希望小紅分到的糖果比他的多,於是在
【HDU4652】Dice(數學期望,動態規劃)
題面 Vjudge 有一個m面骰子 詢問,連續出現n個相同的時候停止的期望 連續出現n個不同的時候停止的期望 題解 考慮兩種分開詢問來算。 第一種: 設f[i]表示已經有連續的i個相
【OpenGL】SOIL(簡易的opengl影象庫)
一、簡介 SOIL是簡易OpenGL影象庫(Simple OpenGL Image Library)的縮寫,它支援大多數流行的影象格式,並且使用簡單。可從官網下載其原始碼:http://www.lonesock.net/soil.html 二、配置 從他們的主頁可以