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

 

暫時這麼多,以後想到再繼續補充。。。

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