1.Collectors.collectingAndThen
- 这是Stream中的一个收集器,相比普通的Collectors.toList、Collectors.groupingBy等收集器
- Collectors.collectingAndThen还可以在收集之后进行某种操作
- 多一个形参,用于写function函数(有入参有出参)
举例说明collectingAndThen:
List按某属性去重,返回List
2.本质流程
使用Collectors.collectingAndThen的本质是:先把new Set( list ) 获取一个Set,然后new List ( set )返回List
3. 如何用Set去重
用TreeSet和HashSet都能去重,只是去重的逻辑不一样。先看二者的存储逻辑:
- Hashset是通过复写hashCode()方法和equals()方法来保证的。
- 而Treeset是通过Compareable接口的compareto方法来保证的。同理可以用Comparator来定制排序
3.1TreeSet去重
①自然排序Comparable接口
@Data
public class DtoReq implements Comparable {
private String userName;
private String password;
@Override
public int compareTo(Object o) {
DtoReq req = (DtoReq)o;
return req.getUserName().compareTo(this.getUserName());
}
}
- 代码逻辑即:对userName字段进行比较,且TreeSet的去重功能不需要关注顺序,谁比谁都一样
- 不过这种方式相当于写死了,耦合度太高
去重代码
ArrayList<DtoReq> collect = arrayList.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(
TreeSet::new),ArrayList::new)
);
new TreeSet(arrayList)时构造器会自动去比较Comparable 接口的compareTo 方法,而达到去重的效果
②定制排序Comparator接口
-
当元素的类型没实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序 -
应该用匿名对象重写compare 方法 或用lambda表达式调用Comparator.comparing 方法 @Data public class DtoReq { private String userName; private String password; }
匿名对象重写compare方法
/**
* 定制排序Comparator对象,封装了排序的逻辑
* 这里的泛型千万不能省,会用作lambda表达式的类型推断
*/
Comparator<DtoReq> comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
DtoReq r1 = (DtoReq)o1;
DtoReq r2 = (DtoReq)o2;
return r1.getUserName().compareTo(r2.getUserName());
}
};
ArrayList<DtoReq> collect = arrayList.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(
() -> new TreeSet<>(comparator)
)
, ArrayList::new)
);
静态方法comparing+lambda表达式
不需要new那个Comparator对象并重写其compare方法,直接调用静态方法Comparator.comparing 方法,其形参是方法引用 ,意为:针对某属性进行排序,当然这里不是为了排序,只是为了去重
ArrayList<DtoReq> collect = arrayList.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(
() -> new TreeSet<>(Comparator.comparing(DtoReq::getUserName))
)
, ArrayList::new)
);
3.2HashSet去重
- 利用HashSet去重,只需要重写其
equals() 和hashCode() 方法 - 而好巧不巧lombok的@data注解提供了这两个方法的重写
lombok引入@data
@Data
public class DtoReq {
private String userName;
private String password;
}
此时就已经有了equals()和hashCode()方法,直接用就好
直接调HashSet构造器
ArrayList<DtoReq> collect = arrayList.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(
() -> new HashSet<>()
)
, ArrayList::new)
);
方法引用进行优化
ArrayList<DtoReq> collect = arrayList.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(
HashSet::new) , ArrayList::new)
);
4.去重逻辑
5.为什么不用distinct()
Stream流有一个distinct()方法可以去重,但是这个distinct()是直接调用当前流中存储对象的equals()方法,而Set会计算哈希值 List去重以及效率分析
|