先说个栗子:如何使用lambda对Object集合去重
假设有个Student1类,需要通过name给给集合去重。
@Data
class Student1 {
private String stuNo;
private String name;
public Student1(String stuNo,String name){
this.name = name;
this.stuNo = stuNo;
}
}
//看看最整洁版的
public class Application1 {
public static void main(String[] args) {
List<Student1> studentList = new ArrayList<>();
studentList.add(new Student1("01","嘻嘻"));
studentList.add(new Student1("02","哈哈"));
studentList.add(new Student1("03","哒哒"));
studentList.add(new Student1("03","哒哒"));
studentList.stream().filter(distinctByKey(Student1::getName));
}
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t->seen.add(keyExtractor.apply(t));
/*Predicate<T> tBooleanCacheLoader = (T t) -> {
Object apply = keyExtractor.apply(t);
return seen.add(apply);
};
return tBooleanCacheLoader; 这段注释和上面的方法意思是一样的*/
}
}
虽然知道他大概意识是什么,通过map来做个去重,那么如果书写这种代码呢,别着急我们先对上面的方法拆解一下。
public static void main(String[] args) {
List<Student1> studentList = new ArrayList<>();
studentList.add(new Student1("01","嘻嘻"));
studentList.add(new Student1("02","哈哈"));
studentList.add(new Student1("03","哒哒"));
studentList.add(new Student1("03","哒哒"));
//这段代码原理和上面得到的结果是一样的
Set<Object> seen = ConcurrentHashMap.newKeySet();
studentList.stream().filter(item->{
return seen.add(item.getName());
});
}
区别:ConcurrentHashMap要在Main方法里面创建。
上面涉及到的:: 和 -> 不明白怎么请看我的另一偏文章。
我们再用一种方式去改善下他们的样子,来得到相同的结果
class TestFunction implements Predicate{
Set<Object> seen = ConcurrentHashMap.newKeySet();
@Override
public boolean test(Object o) {
return seen.add(o);
}
}
public static void main(String[] args) {
List<Student1> studentList = new ArrayList<>();
studentList.add(new Student1("01","嘻嘻"));
studentList.add(new Student1("02","哈哈"));
studentList.add(new Student1("03","哒哒"));
studentList.add(new Student1("03","哒哒"));
TestFunction testFunction = new TestFunction();
studentList.stream().filter(item -> testFunction.test(item));
}
上面这种是不是就很接近第一种方式了,区别:
ConcurrentHashMap不是在main方法里面创建,只是要多创建一个TestFunction类。
我们的方法虽然是在filter里面执行,但是是通过循环调了N遍testFunction.test()
filter(item -> testFunction.test(item))
而第一种方式定义的方法distinctByKey只执行了一次。而且distinctByKey这种代码观赏明显比前者更简洁一点。
filter(distinctByKey(Student1::getName))
那如何定义类型于distinctByKey这种函数呢。首先我们要知道Filter里面的参数是Predicate类型,所以我们定义的方法返回值是Predicate类型,其次为了去创建对象,我们直接把方法修饰符定义成静态的,
那么我们需要待定的是参数和方法内如何写。
private statis <T> Predicate<T> 方法名(参数){
........
}
Predicate这个函数是需要参数的,所以我们在定义方法的时候一定需要把参数传过去,只是这个参数是什么类型,前面我们提到,
如果我们用filter(item -> testFunction.test(item))这种方式,那么我们的方法就要被执行N次。如果是filter(distinctByKey(Student1::getName))这种方式,我们的方法就只需要被执行一次。如果只需要执行一次我们就需要把Student1::getName或者item -> testFunction.test(item) 作为参数传到方法里面去。那么我能就选择Function类型作为参数
这样我们的方法样子就可以确认下来了,
private <T> Predicate<T> 方法名(Function<? super T, ?> keyExtractor){
.......
}
接下来就是写方法内的业务逻辑。
Function类型里面有一个apply方法,通过T得到R
keyExtractor.apply()所以我们通过apply就可以得到我们要执行的
那么要得到Predicate方法就很见到了,看他的方法定义boolean test(T t),那么转成Lambda就是
(T t)->{
.....业务逻辑
return boolean;
}
结合我么需要去重的逻辑就是,把要判断的数据放置到map中去如果,存在返回false,如果不存在返回true
(T t)->{
Object apply = keyExtractor.apply(t);
//得到中间类型,再把这个中间类型放到Map中,看看是否存在
return seen.add(apply);
}
进而就变成了filter(distinctByKey(Student1::getName))或者filter(distinctByKey(item -> testFunction.test(item)))