6,Map集合遍歷的4中方法?
5,List遍歷時如何remove元素
4、漏網之魚-for循環遞增下標方式遍歷集合,並刪除元素
如果你用for循環遞增下標方式遍歷集合,在遍歷過程中刪除元素,你可能會遺漏了某些元素。
3、ConcurrentModificationException異常-----Iterator遍歷集合過程中用其他手段(或其他線程)操作元素
2、Map集合操作陷阱;
1、Set集合操作陷阱---一個不包含重復元素的 collection。更確切地講,set 不包含滿足 e1.equals(e2) 的元素對 e1 和 e2
=====================
6,Map集合遍歷的4中方法?
在Java中如何遍歷Map對象
在java中遍歷Map有不少的方法。我們看一下最常用的方法及其優缺點。
既然java中的所有map都實現了Map接口,以下方法適用於任何map實現(HashMap, TreeMap, LinkedHashMap, Hashtable, 等等)
方法一 在for-each循環中使用entries來遍歷
這是最常見的並且在大多數情況下也是最可取的遍歷方式。在鍵值都需要時使用。
- Map<Integer, Integer> map = new HashMap<Integer, Integer>();
- for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
- System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
- }
注意:for-each循環在Java 5中被引入所以該方法只能應用於java 5或更高的版本中。如果你遍歷的是一個空的map對象,for-each循環將拋出NullPointerException,因此在遍歷前你總是應該檢查空引用。
方法二 在for-each循環中遍歷keys或values。
如果只需要map中的鍵或者值,你可以通過keySet或values來實現遍歷,而不是用entrySet。
- Map<Integer, Integer> map = new HashMap<Integer, Integer>();
- //遍歷map中的鍵
- for (Integer key : map.keySet()) {
- System.out.println("Key = " + key);
- }
- //遍歷map中的值
- for (Integer value : map.values()) {
- System.out.println("Value = " + value);
- }
該方法比entrySet遍歷在性能上稍好(快了10%),而且代碼更加干凈。
方法三使用Iterator遍歷
使用泛型:
- Map<Integer, Integer> map = new HashMap<Integer, Integer>();
- Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
- while (entries.hasNext()) {
- Map.Entry<Integer, Integer> entry = entries.next();
- System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
- }
不使用泛型:
- Map map = new HashMap();
- Iterator entries = map.entrySet().iterator();
- while (entries.hasNext()) {
- Map.Entry entry = (Map.Entry) entries.next();
- Integer key = (Integer)entry.getKey();
- Integer value = (Integer)entry.getValue();
- System.out.println("Key = " + key + ", Value = " + value);
- }
你也可以在keySet和values上應用同樣的方法。
該種方式看起來冗余卻有其優點所在。首先,在老版本java中這是惟一遍歷map的方式。另一個好處是,你可以在遍歷時調用iterator.remove()來刪除entries,另兩個方法則不能。根據javadoc的說明,如果在for-each遍歷中嘗試使用此方法,結果是不可預測的。
從性能方面看,該方法類同於for-each遍歷(即方法二)的性能。
方法四、通過鍵找值遍歷(效率低)
- Map<Integer, Integer> map = new HashMap<Integer, Integer>();
- for (Integer key : map.keySet()) {
- Integer value = map.get(key);
- System.out.println("Key = " + key + ", Value = " + value);
- }
作為方法一的替代,這個代碼看上去更加干凈;但實際上它相當慢且無效率。因為從鍵取值是耗時的操作(與方法一相比,在不同的Map實現中該方法慢了20%~200%)。如果你安裝了FindBugs,它會做出檢查並警告你關於哪些是低效率的遍歷。所以盡量避免使用。
總結
如果僅需要鍵(keys)或值(values)使用方法二。如果你使用的語言版本低於java 5,或是打算在遍歷時刪除entries,必須使用方法三。否則使用方法一(鍵值都要)。
5,List遍歷時如何remove元素?
public class RemoveElementDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("100012011");
list.add("10001201s1");
list.add("10001201s1");
//解決方案:
//1.i--操作
/*for(int i = 0;i < list.size();i++){
String b = list.get(i);
if(b.equals("502323232")){
list.remove(i);
i--;
}
}*/
//2.反向遍歷
/*for(int i = list.size() - 1;i >= 0;i--){
String b = list.get(i);
if(b.equals("502323232")){
list.remove(i);
}
}*/
//解決方案:調用Iterator的remove()方法安全刪除元素,避免異常
Iterator<String> iter = list.iterator();
while(iter.hasNext()){
String b = iter.next();
if(b.equals("100012011")){
iter.remove();
}
}
for(String b : list){
System.out.println(b);
}
}
}
------------
4、漏網之魚-for循環遞增下標方式遍歷集合,並刪除元素
如果你用for循環遞增下標方式遍歷集合,在遍歷過程中刪除元素,你可能會遺漏了某些元素。
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、對於此情況,我一般都從后面開始遍歷,以避免問題:
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);
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、直接從新創建一個集合,重新擺放,但消耗內存,慎用:
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);
}
}
3、ConcurrentModificationException異常-----Iterator遍歷集合過程中用其他手段(或其他線程)操作元素;
ConcurrentModificationException是Java集合的一個快速報錯(fail-fast)機制,防止多個線程同時修改同一個集合的元素。
在用Iterator遍歷集合時,如果你用其他手段(非Iterator自身手段)操作集合元素,就會報ConcurrentModificationException。
用Iterator方式 或 --簡寫的for(Object o : list) {}方式,遍歷集合,修改元素時會報異常:
延伸個小問題,為什么for(Object o : list) {}方式遍歷集合,現象和Iterator方式一樣,都會報錯呢?
答:這是因為Java的糖語法,“for(Object o : list) {}方式”只是Java語言用“易用性糖衣”吸引你的手段,本質上,它也是Iterator。
2、Map集合操作陷阱;
方法1--- 在for-each循環中使用entries來遍歷
這是最常見的並且在大多數情況下也是最可取的遍歷方式。在鍵值都需要時使用。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key="+entry.getKey()+",Value="+entry.getValue());
}
注意:for-each循環在Java 5中被引入所以該方法只能應用於java 5或更高的版本中。如果你遍歷的是一個空的map對象,for-each循環將拋出NullPointerException,因此在遍歷前你總是應該檢查空引用。
方法2 ---在for-each循環中遍歷keys或values。
如果只需要map中的鍵或者值,你可以通過keySet或values來實現遍歷,而不是用entrySet。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//遍歷map中的鍵
for (Integer key : map.keySet()) {
System.out.println("Key = " + key);
}
//遍歷map中的值
for (Integer value : map.values()) {
System.out.println("Value = " + value);
}
該方法比entrySet遍歷在性能上稍好(快了10%),而且代碼更加干凈。
方法3--使用Iterator遍歷
使用泛型:
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<Integer, Integer> entry = entries.next();
System.out.print("Key="+entry.getKey()+",Value="+entry.getValue())
}
不使用泛型:
Map map = new HashMap();
Iterator entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
Integer key = (Integer)entry.getKey();
Integer value = (Integer)entry.getValue();
System.out.println("Key = " + key + ", Value = " + value);
}
你也可以在keySet和values上應用同樣的方法。
該種方式看起來冗余卻有其優點所在。首先,在老版本java中這是惟一遍歷map的方式。
另一個好處是,你可以在遍歷時調用iterator.remove()來刪除entries,另兩個方法則不能。
根據javadoc的說明,如果在for-each遍歷中嘗試使用此方法,結果是不可預測的。從性能方面看,該方法類同於for-each遍歷(即方法二)的性能。
方法4、通過鍵找值遍歷(效率低)
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer key : map.keySet()) {
Integer value = map.get(key);
System.out.println("Key = " + key + ", Value = " + value);
}
作為方法一的替代,這個代碼看上去更加干凈;但實際上它相當慢且無效率。
因為從鍵取值是耗時的操作(與方法一相比,在不同的Map實現中該方法慢了20%~200%)。如果你安裝了FindBugs,它會做出檢查並警告你關於哪些是低效率的遍歷。所以盡量避免使用。
1、Set集合操作陷阱--- 一個不包含重復元素的 collection。更確切地講,set 不包含滿足 e1.equals(e2) 的元素對 e1 和 e2
public class SetTest2 {
public static void 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()+" ");
}
/**
* for增強循環遍歷
*/
for(String value : set){
System.out.print(value+" ");
}
}
}
當然Set集合也可以使用for循環了。
注意:這里Set集合中放入的是String類型,假如我們放入一個自己定義的類實例的時候,比如Person類實例,這時候我們要自己重寫hashcode和equal方法,
因為當使用HashSet時,會先比較對象的hashCode(),如果存儲在集合中的對象的hashCode值是否與增加的對象的hash code值一致;如果不一致,直接加進去;如果一致,再進行equals方法的比較,equals方法如果返回true,表示對象已經加進去了,就不會再增加新的對象,否則加進去。
---Set集合的另外一個重要實現類TreeSet;
TreeSet使用元素的自然順序對元素進行排序,或者--根據創建 set時提供的Comparator 進行排序,具體取決於使用的構造方法。
通俗一點講,就是可以按照排序后的列表顯示,也可以按照指定的規則排序;
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]按照排序后輸出。
那么如果我們想讓他倒序輸出呢?當然方法很多。這里我采用指定一個規則讓他倒序輸出
public class TreeSetTest3 {
public static void main(String[] args) {
Set<String> set = new TreeSet<String>(new MyComparator());
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
set.add("A");
for(Iterator<String> iterator = set.iterator();iterator.hasNext();){
System.out.print(iterator.next()+" ");
}
}
}
class MyComparator implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);//降序排列
}
}
輸出:e d c b a A
如果Set集合中放入的是我們自己定義的一個類類型呢?
注意:一定要定義一個排序規則類實現Comparator接口,與上面的方法類似,因為TreeSet默認是自然排序,如果沒有實現Comparator,那么TreeSet就識別不了。
public class TreeSetTest2 {
public static void main(String[] args) {
Set<Person> set = new TreeSet<Person>(new PersonComparator());
Person p1 = new Person(10);
Person p2 = new Person(20);
Person p3 = new Person(30);
Person p4 = new Person(40);
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
for(Iterator<Person> iterator = set.iterator();iterator.hasNext();){
System.out.print(iterator.next().score+" ");
}
}
}
class Person{
int score;
public Person(int score){
this.score = score;
}
public String toString(){
return String.valueOf(this.score);
}
}
class PersonComparator implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
return o1.score - o2.score;
}
}
輸出:10 20 30 40
如果按照一個人的分數的倒序排列,只需要更改compare方法中的o2.score-o1.score
