Lambda表達式常用代碼示例
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表達式常用代碼示例