lambda表达式常用篇

        这里介绍一下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()将两者之一查出来。

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());
    }

 

暂时这么多,以后想到再继续补充。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章