这里介绍一下lambda表达式,主要是平时写代码常用的,也许不全面,但是常用的我会很详细的介绍的,其原理大家有兴趣可以自己探索,我看中的是它的应用价值,废话不多说,直接进入主题。
lambda表达式形式其实比较简单的,如下,基本上都这样的,更加具体的,看如下代码
() -> statement
() -> {statement}
(param1,param2...) -> statement
(param1,param2...) -> {statement}
定义一个基本的实体类:
package com.chenjianwen.test;
/**
* @Description: <br>
* @Date: Created in 2019/9/23 <br>
* @Author: chenjianwen
*/
public class Userz {
private String phone;
private String name;
private Integer age;
public Userz(){}
public Userz(String phone, String name, Integer age){
this.phone = phone;
this.name = name;
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Userz{" +
"phone='" + phone + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
一、匿名内部类
1)创建线程的时候,为了快捷方便,经常要使用到匿名内部类,如下这样:
@Test
public void test01(){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello world");
}
}).start();
}
这样已经很简洁了,但是使用lambda表达式还可以更加的简洁的,如下这样:
@Test
public void test02(){
new Thread(() -> System.out.println("hello java")).start();
}
2)在使用比较器Comparator时也可以使用lambda表达式
@Test
public void test03(){
List<Userz> userzList = new ArrayList<>();
userzList.add(new Userz("13023344555", "chen1", 12));
userzList.add(new Userz("13023344555", "chen2", 14));
userzList.add(new Userz("13023344555", "chen3", 5));
userzList.add(new Userz("13023344555", "chen4", 67));
System.out.println("排序前");
for(Userz u : userzList){
System.out.println(u.toString());
}
System.out.println("排序后");
Collections.sort(userzList, new Comparator<Userz>() {
@Override
public int compare(Userz o1, Userz o2) {
return o1.getAge() - o2.getAge();
}
});
for(Userz u : userzList){
System.out.println(u.toString());
}
}
其中,这里使用了比较器的:
这里的比较器换成lambda表达式的话,就简便多了,如下:
Collections.sort(userzList, (x,y) -> x.getAge()-y.getAge());
或者这样也可以:
userzList.sort((x,y) -> x.getAge()-y.getAge());
或者这样:
Collections.sort(userzList, Comparator.comparing(Userz::getAge));
二、集合操作
1)集合的遍历
平时我们集合的遍历都使用过foreach方法,如下:
@Test
public void test04(){
List<String> list = Arrays.asList("a","b","c","hello","girl");
for(String s : list){
System.out.println(s);
}
}
现在有了lambda,就可以使用这种方式:
@Test
public void test04(){
List<String> list = Arrays.asList("a","b","c","hello","girl");
list.forEach(y -> System.out.println(y));
}
或者这样:
@Test
public void test04(){
List<String> list = Arrays.asList("a","b","c","hello","girl");
list.forEach(System.out::println);
}
2)filter()的使用
假如上面的list集合我想筛选出长度大于3的字符串,那该怎么办:
如果不使用lambda表达式,需要foreach遍历在判断,如下:
@Test
public void test06(){
List<String> list = Arrays.asList("a","b","c","hello","girl");
List<String> res = new ArrayList<>();
for(String s : list){
if(s.length() > 3){
res.add(s);
}
}
res.forEach(System.out::println);
}
如果使用lambda表达式,就这样:
@Test
public void test07(){
List<String> list = Arrays.asList("a","b","c","hello","girl");
list.stream().filter(x -> x.length() > 3).collect(Collectors.toList()).forEach(System.out::println);
}
是不是感觉简洁许多。
3)map()的使用
有时候已知一个对象的集合,我们需要将这个对象中的某个变量重新放在一个集合中,例如上面的list<Userz>集合中我要获取其变量name的集合list<String>,需要foreach遍历集合,如下:
@Test
public void test08(){
List<Userz> userzList = new ArrayList<>();
userzList.add(new Userz("13023344555", "chen1", 12));
userzList.add(new Userz("13023344555", "chen2", 14));
userzList.add(new Userz("13023344555", "chen3", 5));
userzList.add(new Userz("13023344555", "chen4", 67));
List<String> res = new ArrayList<>();
for(Userz u : userzList){
res.add(u.getName());
}
for(String s : res){
System.out.println(s);
}
}
如果使用lambda表达式就简洁很多:
@Test
public void test09(){
List<Userz> userzList = new ArrayList<>();
userzList.add(new Userz("13023344555", "chen1", 12));
userzList.add(new Userz("13023344555", "chen2", 14));
userzList.add(new Userz("13023344555", "chen3", 5));
userzList.add(new Userz("13023344555", "chen4", 67));
userzList.stream().map(t -> t.getName()).collect(Collectors.toList()).forEach(x -> System.out.println(x));
}
只需上述一行代码即可,
也可以这么写:
userzList.stream().map(Userz::getName).collect(Collectors.toList()).forEach(System.out::println);
4)Predicate的使用
有时候filter()需要多重筛选条件,例如如下字符串集合,我需要筛选出以c开头,且长度大于3的字符串;一共两重筛选条件,而且有可能n多重筛选条件,就需要用到Predicate:
@Test
public void test10(){
List<String> list = Arrays.asList("c", "go", "java", "hello world", "python", "come on", "cnmlgb");
Predicate<String> p1 = (x) -> x.startsWith("c");
Predicate<String> p2 = (y) -> y.length() > 3;
list.stream().filter(p1.and(p2)).forEach(System.out::println);
list.stream().filter(p1.or(p2)).forEach(System.out::println);
}
其中,and()是将两个同时满足条件的查出来,or()将两者之一查出来。
map()和filter()很像,都是筛选,但是map()前后元素个数不变。
5)reduce()的使用
reduce()共有三个重载方法,如下:
第一个方法,假如说Stream中的元素为a[0],a[1],..,a[n],用java代码表示为
T result = a[0];
for(int i = 1;i < n;i++){
result = accumulator.apply(result, a[i]);
}
return result;
他在四则运算,求最值,或字符串拼接等又很多运用,如下演示:
/**
* 求和
*/
@Test
public void test12() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}).get();
System.out.println(value);
}
@Test
public void test13() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce((x, y) -> x + y).get();
System.out.println(value);
}
/**
* 求最大值
*/
@Test
public void test14() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer > integer2 ? integer : integer2;
}
}).get();
System.out.println(value);
}
@Test
public void test15() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce((x, y) -> x > y ? x : y).get();
System.out.println(value);
}
/**
* 字符串拼接
*/
@Test
public void test18(){
Stream<String> s = Stream.of("I", " " ,"am", " ", "the" , " ", "God");
String value = s.reduce(new BinaryOperator<String>() {
@Override
public String apply(String s, String s2) {
return s.concat(s2);
}
}).get();
System.out.println(value);
}
第二个相当于第一个方法有个初始值,算是加强版,用java代码表示如下
T result = identity;
for(int i = 0;i < n;i++){
result = accumulator.apply(result, a[i]);
}
return result;
针对于上述的四则运算,最值和字符串拼接如下
/**
* 求和
*/
@Test
public void test12() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(5, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
});
System.out.println(value);
}
@Test
public void test13() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(5, (x, y) -> x + y);
System.out.println(value);
}
/**
* 求最大值
*/
@Test
public void test14() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(5 ,new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer > integer2 ? integer : integer2;
}
});
System.out.println(value);
}
@Test
public void test15() {
Stream<Integer> s = Stream.of(1, 2, 3, 4);
Integer value = s.reduce(5, (x, y) -> x > y ? x : y);
System.out.println(value);
}
/**
* 字符串拼接
*/
@Test
public void test18(){
Stream<String> s = Stream.of(" " ,"am", " ", "the" , " ", "God");
String value = s.reduce("I", new BinaryOperator<String>() {
@Override
public String apply(String s, String s2) {
return s.concat(s2);
}
});
System.out.println(value);
}
第三个很复杂的
其方法里面又三个参数:
第一个参数:identity,泛型为U,与reduce方法的返回值一致,但是此时Stream中元素的泛型为T,换言之,两者类型不一致,这样的话,操作空间就非常大了,不管Stream中存储的是什么类型,U都可以是任意类型,包括基本类型的包装类,如Integer,Long等,或者是对象类String等,或者是集合ArrayList等。
第二个参数:accumulator,其类型是BiFunction,输入的是U和T两种类型的数据,而返回的是U类型。
第三个参数:combiner,类型是BinaryOperator,支持对U类型的对象进行操作。如果Stream是非并行的,此参数不生效;只有并行时才生效。
1、非并行时,combiner不生效,其java代码表述如下:
U result = identity;
for (T element:a) {
result = accumulator.apply(result, element);
}
return result;
仔细观察,result类型是U,而Element类型是T。如果U与T一致,那么就与上述一个或两个参数的reduce方法一样了。正因为可以不一样,就存在多种用法,假设U的类型是ArrayList,那么可以将Stream中所有元素添加到ArrayList中,如下:
@Test
public void test19(){
Stream<String> s1 = Stream.of("b", "ab", "bbc" ,"ppg");
s1.reduce(new ArrayList<String>(), new BiFunction<ArrayList<String>, String, ArrayList<String>>() {
@Override
public ArrayList<String> apply(ArrayList<String> strings, String s) {
strings.add(s);
return strings;
}
}, new BinaryOperator<ArrayList<String>>() {
@Override
public ArrayList<String> apply(ArrayList<String> strings, ArrayList<String> strings2) {
return strings;
}
}).forEach(System.out::println);
}
其lambda表达式为:
@Test
public void test21(){
Stream<String> s1 = Stream.of("b", "ab", "bbc" ,"ppg");
s1.reduce(new ArrayList<String>(), (x,y) -> {x.add(y);return x;}, (a,b) -> a).forEach(System.out::println);
}
也可以使用Predicate对元素进行过滤:
@Test
public void test20(){
Stream<String> s1 = Stream.of("b", "ab", "bbc" ,"ppg");
Predicate<String> predicate = t -> t.contains("b");
s1.reduce(new ArrayList<String>(), new BiFunction<ArrayList<String>, String, ArrayList<String>>() {
@Override
public ArrayList<String> apply(ArrayList<String> strings, String s) {
if(predicate.test(s)){
strings.add(s);
}
return strings;
}
}, new BinaryOperator<ArrayList<String>>() {
@Override
public ArrayList<String> apply(ArrayList<String> strings, ArrayList<String> strings2) {
return strings;
}
}).forEach(System.out::println);
}
其lambda表达式为:
@Test
public void test22(){
Stream<String> s1 = Stream.of("b", "ab", "bbc" ,"ppg");
Predicate<String> predicate = t -> t.contains("b");
s1.reduce(new ArrayList<String>(), (x,y) -> {if(predicate.test(y)) x.add(y);return x;}, (a,b) -> a).forEach(System.out::println);
}
2、并行时,第三个参数就有意义了,它将不同线程计算的结果调用combiner做汇总后返回,由于采用了并行计算,与非并行时计算的结果也有了差异,例如,计算1+2+3+4的值,其中4为初始值:
@Test
public void test23(){
System.out.println(Stream.of(1,2,3).parallel().reduce(4, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}));
}
/**
* lambda表达
*/
@Test
public void test24(){
System.out.println(Stream.of(1,2,3).parallel().reduce(4, (x,y) -> x+y, (a,b) -> a+b));
}
计算结果应该是10的才对,然而实际结果时18,实际计算结果是这样的:
对于第一个BiFunction重写的apply()方法,线程一:4+1=5,线程2:4+2=6,线程3:4+3=7
然后对于第二个BinaryOperator重写方法apply()方法将上述结果相加:5+6+7=18。
换个测验,将BinaryOperator重写方法apply()方法换成相乘
@Test
public void test23(){
System.out.println(Stream.of(1,2,3).parallel().reduce(4, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
}, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer * integer2;
}
}));
}
/**
* lambda表达
*/
@Test
public void test24(){
System.out.println(Stream.of(1,2,3).parallel().reduce(4, (x,y) -> x+y, (a,b) -> a*b));
}
其运算过程是:(4+1)*(4+2)*(4+3) = 210;
其效果与下列写法一致:
@Test
public void test25(){
System.out.println(Stream.of(1,2,3).map(x -> x+4).reduce((y,z) -> y*z).get());
}
6)flatMap()将多个集合合成一个集合
@Test
public void test112(){
Stream<List<Integer>> listStream = Stream.of(Arrays.asList(1, 2), Arrays.asList(2, 3, 4, 5));
listStream.flatMap(x -> x.stream()).forEach(System.out::println);
}
7)使用distinct对元素进行去重
@Test
public void test113(){
Stream<String> you = Stream.of("you", "and", "me", "and");
you.distinct().forEach(str -> System.out.println(str));
}
8)使用sorted进行排序
@Test
public void test114(){
Stream<Integer> sort = Stream.of(4, 2, 8, 1, 5, 2, 9);
sort.sorted((x, y) -> y-x).forEach(z -> System.out.print(z));
}
9)collect()转换成具体的集合
@Test
public void test118(){
Stream<String> stream = Stream.of("I", "love", "you", "too");
// List<String> list = stream.collect(Collectors.toList()); // (1)
// Set<String> set = stream.collect(Collectors.toSet()); // (2)
//指定集合类型
ArrayList<String> list = stream.collect(Collectors.toCollection(ArrayList::new));
// HashSet<String> set = stream.collect(Collectors.toCollection(HashSet::new));
System.out.println(list);
// System.out.println(set);
}
10)使用join拼接
@Test
public void test119(){
Stream<String> stream = Stream.of("I", "love", "you", "too");
// String collect = stream.collect(Collectors.joining()); // Iloveyoutoo
// String collect = stream.collect(Collectors.joining(",")); // I,love,you,too
String collect = stream.collect(Collectors.joining(",", "{", "}")); // {I,love,you,too}
System.out.println(collect);
}