1. 程式人生 > >Lambda表達式常用代碼示例

Lambda表達式常用代碼示例

str tag compare 上下文 數組 img 接受 聲明 java語言

Lambda表達式常用代碼示例

2017-10-24

目錄

1 Lambda表達式是什麽
2 Lambda表達式語法
3 函數式接口是什麽
3.1 常用函數式接口
4 Lambdas和Streams結合使用
4.1 使用forEach方法
4.2 使用過濾器filter方法
4.3 使用limit方法
4.4 使用sorted方法
4.5 使用map方法
4.6 使用parallelStream方法
4.7 使用summaryStatistics方法
參考

1 Lambda表達式是什麽


返回

Lambda 表達式是一種匿名函數(對 Java 而言這並不完全正確。在Java中,Lambda 表達式是對象,他們必須依附於一類特別的對象類型——函數式接口(functional interface))。Lambda表達式是Java SE 8中一個重要的新特性。lambda表達式就和方法一樣,它提供了一個正常的參數列表(argument)和一個使用這些參數的主體(body)。

Lambda 表達式為 Java 添加了缺失的函數式編程特點,使我們能將函數當做一等公民看待。

Lambda表達式還增強了集合庫。 Java SE 8添加了2個對集合數據進行批量操作的包: java.util.function 包以及java.util.stream 包。 流(stream)就如同叠代器(iterator),但附加了許多額外的功能。 總的來說,lambda表達式和 stream 是自Java語言添加泛型(Generics)和註解(annotation)以來最大的變化。

2 Lambda表達式語法


返回

Java 中的 Lambda 表達式通常使用 (argument) -> (body)

語法書寫,例如:

(arg1, arg2...) -> { body }
(type1 arg1, type2 arg2...) -> { body }

以下是一些 Lambda 表達式的例子:

(int a, int b) -> {  return a + b; }
() -> System.out.println("Hello World");
(String s) -> { System.out.println(s); }
() -> 42
() -> { return 3.1415 };

Lambda表達式結構說明:

  • 一個 Lambda 表達式可以有零個或多個參數
  • 參數的類型既可以明確聲明,也可以根據上下文來推斷。例如:(int a)與(a)效果相同
  • 所有參數需包含在圓括號內,參數之間用逗號相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
  • 空圓括號代表參數集為空。例如:() -> 42
  • 當只有一個參數,且其類型可推導時,圓括號()可省略。例如:a -> return a*a
  • Lambda 表達式的主體可包含零條或多條語句
  • 如果 Lambda 表達式的主體只有一條語句,花括號{}可省略。匿名函數的返回類型與該主體表達式一致
  • 如果 Lambda 表達式的主體包含一條以上語句,則表達式必須包含在花括號{}中(形成代碼塊)。匿名函數的返回類型與代碼塊的返回類型一致,若沒有返回則為空

3 函數式接口是什麽


返回

函數式接口是只包含一個抽象方法聲明的接口。

java.lang.Runnable 就是一種函數式接口,在 Runnable 接口中只聲明了一個方法 void run(),相似地,ActionListener 接口也是一種函數式接口,我們使用匿名內部類來實例化函數式接口的對象,有了 Lambda 表達式,這一方式可以得到簡化。Runnable接口代碼如下:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

每個 Lambda 表達式都能隱式地賦值給函數式接口。如下代碼:

Runnable r = () -> System.out.println("hello world");

Runnable的Lambda表達式和匿名內部類的使用示例

技術分享
public void sample01() {
        // 1.1使用匿名內部類
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("1.1 Hello world !");
            }
        }).start();

        // 1.2使用 lambda expression
        new Thread(() -> System.out.println("1.2 Hello world !")).start();

        // 2.1使用匿名內部類
        Runnable race1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("1.3 Hello world !");
            }
        };

        // 2.2使用 lambda expression
        Runnable race2 = () -> System.out.println("1.4 Hello world !");

        // 直接調用 run 方法(沒開新線程哦!)
        race1.run();
        race2.run();
    }
View Code

自定義函數式接口及Lambda表達式和匿名內部類的使用示例

WorkerInterface.java

技術分享
//定義一個函數式接口
@FunctionalInterface
public interface WorkerInterface {
    public void doSomeWork();
}
View Code

WorkerInterfaceTest.java

技術分享
public class WorkerInterfaceTest {
    public static void main(String [] args) {
        //invoke doSomeWork using Annonymous class
        execute(new WorkerInterface() {
            @Override
            public void doSomeWork() {
                System.out.println("Worker invoked using Anonymous class");
            }
        });

        //invoke doSomeWork using Lambda expression
        execute( () -> System.out.println("Worker invoked using Lambda expression") );
    }

    public static void execute(WorkerInterface worker) {
        worker.doSomeWork();
    }
}
View Code

3.1 常用函數式接口

Predicate、Function、Consumer、Supplier示例代碼:

技術分享
        //Predicate 接口只有一個參數,返回boolean類型。。
        Predicate<String> predicate = (s) -> s.length() > 3;
        System.out.println(predicate.test("foo"));   // false
        Predicate<Boolean> nonNull = Objects::nonNull;
        System.out.println(nonNull.test(false));     // true

        //Function 接口接受一個參數,返回一個結果,並附帶了一些可以和其他函數組合的默認方法(compose, andThen)
        Function<String, Integer> toInteger = Integer::valueOf;
        System.out.println(toInteger.apply("123").getClass());    //class java.lang.Integer
        Function<String, String> backToString = toInteger.andThen(String::valueOf);
        System.out.println(backToString.apply("123").getClass()); //class java.lang.String

        //Consumer 接口接受一個參數,沒有返回結果。
        Consumer<Cat> greeter = (c) -> System.out.println("Hello, " + c.name);
        Cat cat=new Cat("Tom", 3);
        greeter.accept(cat);    //Hello, Tom

        //Supplier接口沒有參數,。
        Supplier<Double> number = () -> Math.random();
        System.out.println(number.get());  //0.8765252430762529
View Code

使用Lambdas排序集合

在Java中,Comparator 類被用來排序集合。

使用匿名內部類或Lambda表達式根據 name 排序 players:

技術分享
        String[] players = {"Rafael Nadal", "Novak Djokovic", "Stanislas Wawrinka", "David Ferrer",
                            "Roger Federer", "Andy Murray", "Tomas Berdych", "Juan Martin Del Potro",
                            "Richard Gasquet", "John Isner"};

        // 1.1 使用匿名內部類根據 name 排序 players
        Arrays.sort(players, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return (s1.compareTo(s2));
            }
        });

        // 1.2 使用 lambda expression 排序 players
        Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
        Arrays.sort(players, sortByName);

        // 1.3 也可以采用如下形式:
        Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));
View Code

其他排序:

技術分享
        // 2.1 使用匿名內部類根據 surname 排序 players
        Arrays.sort(players, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));
            }
        });

        // 2.2 使用 lambda expression 排序,根據 surname
        Comparator<String> sortBySurname = (String s1, String s2) ->
                ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) );
        Arrays.sort(players, sortBySurname);

        // 2.3 或者這樣,懷疑原作者是不是想錯了,括號好多...
        Arrays.sort(players, (String s1, String s2) ->
                ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) )
        );

        // 3.1 使用匿名內部類根據 name lenght 排序 players
        Arrays.sort(players, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return (s1.length() - s2.length());
            }
        });

        // 3.2 使用 lambda expression 排序,根據 name lenght
        Comparator<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length());
        Arrays.sort(players, sortByNameLenght);

        // 3.3 or this
        Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));

        // 4.1 使用匿名內部類排序 players, 根據最後一個字母
        Arrays.sort(players, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
            }
        });

        // 4.2 使用 lambda expression 排序,根據最後一個字母
        Comparator<String> sortByLastLetter =
                (String s1, String s2) ->
                        (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
        Arrays.sort(players, sortByLastLetter);

        // 4.3 or this
        Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));
View Code

4 Lambdas和Streams結合使用


返回

Stream是對集合的包裝,通常和lambda一起使用。 使用lambdas可以支持許多操作,如 map, filter, limit, sorted, count, min, max, sum, collect 等等。 同樣,Stream使用懶運算,他們並不會真正地讀取所有數據,遇到像getFirst() 這樣的方法就會結束鏈式語法。

Person.java代碼:

技術分享
public class Person {
    private String firstName;
    private String lastName;
    private String job;
    private String gender;
    private int salary;
    private int age;

    public Person(String firstName, String lastName, String job,
                  String gender, int age, int salary) {
        this.setFirstName(firstName);
        this.setLastName(lastName);
        this.setGender(gender);
        this.setAge(age);
        this.setJob(job);
        this.setSalary(salary);
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
View Code

初始化Person代碼:

技術分享
       List<Person> javaProgrammers = new ArrayList<Person>() {
            {
                add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 43, 2000));
                add(new Person("Tamsen", "Brittany", "Java programmer", "female", 23, 1500));
                add(new Person("Floyd", "Donny", "Java programmer", "male", 33, 1800));
                add(new Person("Sindy", "Jonie", "Java programmer", "female", 32, 1600));
                add(new Person("Vere", "Hervey", "Java programmer", "male", 22, 1200));
                add(new Person("Maude", "Jaimie", "Java programmer", "female", 27, 1900));
                add(new Person("Shawn", "Randall", "Java programmer", "male", 30, 2300));
                add(new Person("Jayden", "Corrina", "Java programmer", "female", 35, 1700));
                add(new Person("Palmer", "Dene", "Java programmer", "male", 33, 2000));
                add(new Person("Addison", "Pam", "Java programmer", "female", 34, 1300));
            }
        };

        List<Person> phpProgrammers = new ArrayList<Person>() {
            {
                add(new Person("Jarrod", "Pace", "PHP programmer", "male", 34, 1550));
                add(new Person("Clarette", "Cicely", "PHP programmer", "female", 23, 1200));
                add(new Person("Victor", "Channing", "PHP programmer", "male", 32, 1600));
                add(new Person("Tori", "Sheryl", "PHP programmer", "female", 21, 1000));
                add(new Person("Osborne", "Shad", "PHP programmer", "male", 32, 1100));
                add(new Person("Rosalind", "Layla", "PHP programmer", "female", 25, 1300));
                add(new Person("Fraser", "Hewie", "PHP programmer", "male", 36, 1100));
                add(new Person("Quinn", "Tamara", "PHP programmer", "female", 21, 1000));
                add(new Person("Alvin", "Lance", "PHP programmer", "male", 38, 1600));
                add(new Person("Evonne", "Shari", "PHP programmer", "female", 40, 1800));
            }
        };
View Code

4.1 使用forEach方法

使用forEach方法顯示所有程序員姓名:

技術分享
        System.out.println("所有程序員的姓名:");
        javaProgrammers.forEach((p) -> System.out.println(String.format("%s %s; ", p.getFirstName(), p.getLastName())));
        phpProgrammers.forEach((p) -> System.out.println(String.format("%s %s; ", p.getFirstName(), p.getLastName())));
View Code

ForEach代碼:

技術分享
    public interface Iterable<T> {
        default void forEach(Consumer<? super T> action) {
            Objects.requireNonNull(action);
            for (T t : this) {
                action.accept(t);
            }
        }
    ...
    }
View Code

使用forEach方法,增加程序員的工資5%:

技術分享
        System.out.println("給程序員加薪 5% :");
        Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
        javaProgrammers.forEach(giveRaise);
        phpProgrammers.forEach(giveRaise);
View Code

4.2 使用過濾器filter方法

技術分享
        System.out.println("下面是月薪超過 $1,400 的PHP程序員:");
        phpProgrammers.stream()
                .filter((p) -> (p.getSalary() > 1400))
                .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

        // 定義 filters  
        Predicate<Person> ageFilter = (p) -> (p.getAge() > 25);
        Predicate<Person> salaryFilter = (p) -> (p.getSalary() > 1400);
        Predicate<Person> genderFilter = (p) -> ("female".equals(p.getGender()));

        System.out.println("下面是年齡大於 25歲且月薪在$1,400以上的女PHP程序員:");
        phpProgrammers.stream()
                .filter(ageFilter)
                .filter(salaryFilter)
                .filter(genderFilter)
                .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
View Code

4.3 使用limit方法

使用limit方法可以限制結果集的個數:

技術分享
        System.out.println("最前面的3個 Java programmers:");
        javaProgrammers.stream()
                .limit(3)
                .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));


        System.out.println("最前面的3個女性 Java programmers:");
        javaProgrammers.stream()
                .filter(genderFilter)
                .limit(3)
                .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
View Code

4.4 使用sorted方法

使用sorted方法進行排序:

技術分享
        System.out.println("根據 name 排序,並顯示前5個 Java programmers:");
        List<Person> sortedJavaProgrammers = javaProgrammers
                .stream()
                .sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
                .limit(5)
                .collect(toList());

        sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));

        System.out.println("根據 salary 排序 Java programmers:");
        sortedJavaProgrammers = javaProgrammers
                .stream()
                .sorted( (p, p2) -> (p.getSalary() - p2.getSalary()) )
                .collect( toList() );
        sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
View Code

使用min和max方法:

技術分享
        System.out.println("工資最低的 Java programmer:");
        Person pers = javaProgrammers
                .stream()
                .min((p1, p2) -> (p1.getSalary() - p2.getSalary()))
                .get();

        System.out.printf("Name: %s %s; Salary: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary());

        System.out.println("工資最高的 Java programmer:");
        Person person = javaProgrammers
                .stream()
                .max((p, p2) -> (p.getSalary() - p2.getSalary()))
                .get();

        System.out.printf("Name: %s %s; Salary: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary());
View Code

4.5 使用map方法

結合 map 方法,我們可以使用 collect 方法來將我們的結果集放到一個字符串,一個 Set 或一個TreeSet中:

技術分享
        System.out.println("將 PHP programmers 的 first name 拼接成字符串:");
        String phpDevelopers = phpProgrammers
                .stream()
                .map(Person::getFirstName)
                .collect(joining(" ; ")); // 在進一步的操作中可以作為標記(token)
        System.out.println(phpDevelopers);  //Jarrod ; Clarette ; Victor ; Tori ; Osborne ; Rosalind ; Fraser ; Quinn ; Alvin ; Evonne

        System.out.println("將 Java programmers 的 first name 存放到 Set:");
        Set<String> javaDevFirstName = javaProgrammers
                .stream()
                .map(Person::getFirstName)
                .collect(toSet());
        System.out.println(javaDevFirstName);  //[Elsdon, Shawn, Palmer, Addison, Maude, Floyd, Vere, Tamsen, Jayden, Sindy]

        System.out.println("將 Java programmers 的 last name 存放到 TreeSet:");
        TreeSet<String> javaDevLastName = javaProgrammers
                .stream()
                .map(Person::getLastName)
                .collect(toCollection(TreeSet::new));
        System.out.println(javaDevLastName);  //[Brittany, Corrina, Dene, Donny, Hervey, Jaimie, Jaycob, Jonie, Pam, Randall]
View Code

4.6 使用parallelStream方法

當需要為多核系統優化時,可以 parallelStream().forEach(),只是此時原有元素的次序沒法保證,並行的情況下將改變串行時操作的行為:

技術分享
        System.out.println("計算付給 Java programmers 的所有money:");
        int totalSalary = javaProgrammers
                .parallelStream()
                .mapToInt(p -> p.getSalary())
                .sum();
View Code

4.7 使用summaryStatistics方法

使用summaryStatistics方法獲得stream 中元素的各種匯總數據:

技術分享
       //計算 count, min, max, sum, and average for numbers
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        IntSummaryStatistics stats = numbers
                .stream()
                .mapToInt((x) -> x)
                .summaryStatistics();

        System.out.println("List中最大的數字 : " + stats.getMax());
        System.out.println("List中最小的數字 : " + stats.getMin());
        System.out.println("所有數字的總和   : " + stats.getSum());
        System.out.println("所有數字的平均值 : " + stats.getAverage());
View Code

參考

[1] 深入淺出 Java 8 Lambda 表達式

[2] Java中Lambda表達式的使用

[3] Java 8 中的 Streams API 詳解

[4] Lambda 表達式講解

Lambda表達式常用代碼示例