去重操作是开发中经常会碰到的问题,可以自己写一个,也可以直接用Java提供的集合API来实现,毕竟它数据结构和算法都是最优的,所以可以直接拿来用。
众所周知,Java的集合Set有去重的作用。它有很多子类,最常用的就是HashSet和TreeSet。HashSet是无序的,TreeSet则是有序的。
看一个HashSet的简单例子
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(1);
list.add(3);
HashSet hashSet = new HashSet<>(list);
hashSet.stream().forEach(e -> System.out.println(e));
}
}
可以实现去重。我们必须还要保证原有集合中的元素顺序,所以必须使用TreeSet。
TreeSet 描述文档
The elements are ordered using their {@linkplain Comparable natural
ordering}, or by a {@link Comparator} provided at set creation
time, depending on which constructor is used
它有两种排序,一种是自然排序,一种是在构造集合的时候,一种是通过Comparator指定排序。看一下它的构造函数:
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
测试
public static void main(String[] args) {
Map<String,String> map1 = new HashMap<>();
map1.put("id","1");
map1.put("name","张三");
Map<String,String> map2 = new HashMap<>();
map2.put("id","1");
map2.put("name","李四");
Map<String,String> map3 = new HashMap<>();
map3.put("id","2");
map3.put("name","王五");
TreeSet<Map<String,String>> treeSet = new TreeSet<>(Comparator.comparing(map -> String.valueOf(map.get("id"))));
treeSet.add(map1);
treeSet.add(map2);
treeSet.add(map3);
treeSet.stream().forEach(map -> System.out.println(map.get("id") + "==" + map.get("name")));
}
控制台打印
可以看到,放入treeSet中的元素根据id去重了。
上面的TreeSet是一个一个把元素插进去的,现在结合Lambda表达式的函数式接口进行流式编写,对上面的代码进行改造:
public static void main(String[] args) {
Map<String,String> map1 = new HashMap<>();
map1.put("id","1");
map1.put("name","张三");
Map<String,String> map2 = new HashMap<>();
map2.put("id","1");
map2.put("name","李四");
Map<String,String> map3 = new HashMap<>();
map3.put("id","2");
map3.put("name","王五");
List<Map<String, String>> list = new ArrayList<>();
list.add(map1);
list.add(map2);
list.add(map3);
list.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(
s -> String.valueOf(s.get("id"))))), ArrayList::new));
}
流式编写的代码阅读起来确实不方便,而且调试起来也不方便,但它却大大减少代码行数,写起来很有感觉,一气呵成。把上面的代码分开看,就容易理解。
new TreeSet 这一部分就不看,上面已经说过了,就是构造了TreeSet实例。主要看两个函数:
第一个函数
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
Function<R,RR> finisher)
简单来说就把一个集合结构调整为另一个集合结构。例如把一个可变的List变成一个不可变的List。就如文档中写的这个Demo.
List<String> people = people.stream().collect(collectingAndThen(toList(),Collections::unmodifiableList));
第二个函数
public static <T, C extends Collection<T>>
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
return new CollectorImpl<>(collectionFactory, Collection<T>::add,
(r1, r2) -> { r1.addAll(r2); return r1; },
CH_ID);
}
对输入的元素进行计算并放入一个新的集合。新的集合就是我们上面说的那个TreeSet。
总结
集合中的元素就像流水,把一个水桶中水流入另一个水桶,中间有一个净化器。Lambda就像这个净化器。