原文地址:https://www.jianshu.com/p/9d384c0f9ec8
在編程實踐中,經常會遇到這樣的需求場景:遍歷一個集合,把其中不滿足某些條件的元素刪掉。
下面我們寫幾個方法來嘗試用不同的方式來實現這個需求:
先准備兩個校驗方法
/**
* 校驗一個字符串是否合法的方法,字符串的長度大於等於6才是合法的
*
* @param str
* @return
*/
public static boolean isValid(String str) {
if(null == str || str.length() < 6) {
return false;
}
return true;
}
public static boolean isNotValid(String str) {
return !isValid(str);
}
方式1:在for-each循環中刪除列表元素
public static void method1() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("abc");
list.add(null);
list.add("123456");
list.add("");
for(String str : list) {
if(isNotValid(str)) {
list.remove(str);
}
}
System.out.println(list);
System.out.println(list.hashCode());
}
運行上面這個方法,報異常:
java.util.ConcurrentModificationException
可見,並不能在for-each循環遍歷一個列表時去刪除這個列表的元素,此種方法行不通。
方式2:使用迭代器刪除列表元素
public static void method2() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("abc");
list.add(null);
list.add("123456");
list.add("");
System.out.println(list);
System.out.println(list.hashCode());
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String str = it.next();
if(isNotValid(str)) {
it.remove();
}
}
System.out.println(list);
System.out.println(list.hashCode());
}
運行這個方法,輸出:
[1, abc, null, 123456,]
667562667
[123456]
1450575490
可見使用迭代器可以在遍歷列表的同時正常的刪除列表的元素,並且刪除元素之后列表的內存地址已經發生了變化。
方式3:使用Lambda表達式刪除列表元素
上面使用迭代器的方式雖然能夠正常的刪除列表中的元素,但是不夠優雅,因為要寫好幾行的遍歷代碼,顯得略臃腫。能不能只用一行代碼完成這個功能呢?答案是可以的——使用Lambda表達式:
public static void method3() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("abc");
list.add(null);
list.add("123456");
list.add("");
System.out.println(list);
System.out.println(list.hashCode());
list.removeIf(e -> isNotValid(e));
System.out.println(list);
System.out.println(list.hashCode());
}
運行上面的方法,輸出:
[1, abc, null, 123456, ]
667562667
[123456]
1450575490
可見使用Lambda表達式的方法更為優雅,這里使用了List接口所繼承的Collection接口在JDK1.8新增的removeIf方法,該方法接收一個Predicate類型的參數,刪除列表中滿足Predicate條件的元素。在這里使用Lambda表達式:e -> isNotValid(e) 定義了這樣一個Predicate函數。
方式4:使用方法引用刪除列表元素
除了Lambda表達式,JDK1.8還可以用稱為方法引用的方式來刪除列表中的元素,使用類似C++的 :: 運算符,來引用一個對象的實例方法或一個類的類方法,下面就用方法引用的方式來刪除一個列表中的指定元素:
public static void method4() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("abc");
list.add(null);
list.add("123456");
list.add("");
System.out.println(list);
System.out.println(list.hashCode());
list.removeIf(RemoveListElement::isNotValid); // isNotValid為RemoveListElement類的一個靜態方法
System.out.println(list);
System.out.println(list.hashCode());
}
運行上面的方法,輸出:
[1, abc, null, 123456, ]
667562667
[123456]
1450575490
可見使用方法引用的方式也可以達到同樣的目的,但無疑比Lambda表達式更為簡潔、優雅。
總結
經過上述對比,可以看出:
- 在JDK1.8之前,要使用迭代器的方式才能在遍歷一個列表的時候正確的刪除列表的元素。
- 在JDK1.8之后,還可以使用Lambda表達式和方法引用的方式正確的刪除列表中的元素,這兩種方式更為優雅。