在使用java8的stream流對List集合操作時,遇到了去重問題。原有的distinct()方法需要重寫對象的equals()和hashCode()方法,比較麻煩,而且寫在實體裏比較難看。於是查閱資料,寫了如下代碼:
List<User> users = Lists.newArrayList(new User("aa",11),new User("bb",13),new User("aa",14) );
List<User> collect = users.stream().filter(distinctByKey(User::getName)).collect(Collectors.toList());
System.out.println(collect);
//去重函數
public <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> set = ConcurrentHashMap.newKeySet();
System.out.println("----------");
return t -> set.add(keyExtractor.apply(t));
}
運行結果
----------
[User(name=aa, age=11), User(name=bb, age=13)]
巧妙的運用了set.add()方法返回boolean值來去重。根據這個思路我寫了如下代碼:
首先重寫User類的equals()與hashCode()方法
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof User)) {
return false;
}
User user = (User) o;
return Objects.equal(getName(), user.getName());
}
@Override
public int hashCode() {
return Objects.hashCode(getName());
}
List<User> collect1 = users.stream().filter((user) -> {
Set<User> set = ConcurrentHashMap.newKeySet();
System.out.println("----------");
return set.add(user);
}).collect(Collectors.toList());
System.out.println(collect1);
運行結果
----------
----------
----------
[User(name=aa, age=11), User(name=bb, age=13), User(name=aa, age=14)]
去重失敗,結果打印了三次“-----”,說明new Set()被執行了三次,每次filter都new了一個Set。那爲什麼distinctByKey()方法中的 Set set = ConcurrentHashMap.newKeySet()只執行了一次呢?這裏,我認爲雖然distinctByKey()方法作爲參數傳遞給了filter()方法,但distinctByKey()本身就是一個方法,在執行時也有自己的方法棧。filter()方法棧只是使用了distinctByKey()方法棧的boolean類型的返回值。每次filter(),將用user.getName()值傳遞進distinctByKey()方法棧,入棧計算後,將棧頂的boolean結果返回給filter()方法。因此,distinctByKey()方法棧只需在創建它自己的棧的時候執行一次 ConcurrentHashMap.newKeySet()操作。lambda表達式直接當做了filter()的變量進行入棧操作了,所以會執行多次。於是我改寫了代碼,將new Set()放在了lambda表達式外面。
Set<User> set = ConcurrentHashMap.newKeySet();
List<User> collect1 = users.stream().filter((user) -> {
System.out.println("----------");
return set.add(user);
}).collect(Collectors.toList());
System.out.println(collect1);
運行結果
----------
----------
----------
[User(name=aa, age=11), User(name=bb, age=13)]
在不重寫User類的equals()與hashCode()方法時,這樣寫:
Set<String> set = ConcurrentHashMap.newKeySet();
List<User> collect1 = users.stream().filter((user) -> {
System.out.println("----------");
return set.add(user.getName());
}).collect(Collectors.toList());
System.out.println(collect1);
運行結果
----------
----------
----------
[User(name=aa, age=11), User(name=bb, age=13)]