Java8的List Object 去重
- 2020 年 1 月 21 日
- 筆記
假設Object為User,此處User類中省略getting/setting以及相關構造方法。
public class User { private Long id; private String name; private Windcoder com; ...... }
現有List<User> userLiset1
與List<User> userList2
兩個List。
重寫equals()
最簡單的就是重寫User的equals()方法和hashCode()方法:
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (!id.equals(user.id)) return false; return name.equals(user.name); } @Override public int hashCode() { int result = id.hashCode(); result = 31 * result + name.hashCode(); return result; }
之後通過HashSet或stream+contains去重。List 的contains()方法底層實現使用對象的equals方法去比較的,其實重寫equals()就好,但重寫了equals最好將hashCode也重寫了。
set介面是通過equals來判斷是否重複的,HashSet是一種加快判斷效率的一種實現,先通過hashCode判斷(hashCode通過運算求出數組下標,通過下標判斷是否有對象存在),如果重複,再equal比較。故用HashSet去重時必須重寫這兩個方法。
HashSet去重
假設只對userLiset1去重,先將userLiset1轉為HashSet,再轉回List即可:
Set<User> us = new HashSet(); us.addAll(userLiset1); List<User> newUsers = new ArrayList<User>(us);
stream去重
此為Java8始有的方式stream+lambdas:
List<user> newUsers = new ArrayList<User>(); userLiset1.stream().forEach( user -> { if (!personList.contains(user)) { newUsers.add(user); } } );
不重寫equals()
實際上很多時候項目中不能或不允許重寫對象的equals(),此時去重的核心就是通過TreeSet或ConcurrentSkipListSet去重,兩者主要區別是後者為執行緒安全的。
非stream形式
Set<User> userSet = new TreeSet<User>(new Comparator<User>(){ @Override public int compare(User o1, User o2) { return o1.getId().compareTo(o2.getId()); } }) ; userSet.addAll(userLiset1); List<User> newUsers = new ArrayList<User>(userSet);
stream形式
List<User> newUsers = userLiset1.stream().collect( Collectors.collectingAndThen(Collectors.toCollection( () -> new TreeSet<>( Comparator.comparing(User::getId))), ArrayList::new) );
若根據User下的Windcoder的id做比較,可以將上面中的Comparator.comparing比較條件改為:
Comparator.comparing( user->user.getCom().getId()))), ArrayList::new)
collect() 操作會把其接收的元素聚集(aggregate)到一起(這裡是 List)。
兩個List合併及去重
可以使用thenComparing對判重條件進行追加,程式會自動依次對比。
Comparator<User> c=Comparator.comparing(user->user.getCom().getId()) .thenComparing(User::getName); List<User> result = Stream.concat(userLiset1.stream(), userLiset2.stream()) .filter(new ConcurrentSkipListSet<>(c)::add) .collect(Collectors.toList());
- concat用於拼接流,這裡是拼接兩個
List<User>
流。 - Collectors 類實現了很多歸約操作,例如將流轉換成集合和聚合元素。Collectors 可用於返回列表或字元串,這裡返回的是List;
- filter 方法用於通過設置的條件過濾出元素,這裡相當於過濾掉重複的User,重複的後者將被捨棄。