1. 程式人生 > >Java8新特性--Lambda表達式

Java8新特性--Lambda表達式

stream 標記 計算 cat ble chm chan 需要 sla

從java8出現以來lambda是最重要的特性之一,它可以讓我們用簡潔流暢的代碼完成一個功能。 很長一段時間java被吐槽是冗余和缺乏函數式編程能力的語言,隨著函數式編程的流行java8種也引入了 這種編程風格。

技術分享圖片

什麽是lambda?

lambda表達式是一段可以傳遞的代碼,它的核心思想是將面向對象中的傳遞數據變成傳遞行為。 我們回顧一下在使用java8之前要做的事,之前我們編寫一個線程時是這樣的:

1 2 3 4 5 6 Runnable r = new Runnable() { @Override public void run() {
System.out.println("do something."); } }

也有人會寫一個類去實現Runnable接口,這樣做沒有問題,我們註意這個接口中只有一個run方法, 當把Runnable對象給Thread對象作為構造參數時創建一個線程,運行後將輸出do something.。 我們使用匿名內部類的方式實現了該方法。

這實際上是一個代碼即數據的例子,在run方法中是線程要執行的一個任務,但上面的代碼中任務內容已經被規定死了。 當我們有多個不同的任務時,需要重復編寫如上代碼。

設計匿名內部類的目的,就是為了方便 Java 程序員將代碼作為數據傳遞。不過,匿名內部 類還是不夠簡便。 為了執行一個簡單的任務邏輯,不得不加上 6 行冗繁的樣板代碼。那如果是lambda該怎麽做?

1 Runnable r = () -> System.out.println("do something.");

嗯,這代碼看起來很酷,你可以看到我們用()和->的方式完成了這件事,這是一個沒有名字的函數,也沒有人和參數,再簡單不過了。 使用->將參數和實現邏輯分離,當運行這個線程的時候執行的是->之後的代碼片段,且編譯器幫助我們做了類型推導; 這個代碼片段可以是用{}包含的一段邏輯。下面一起來學習一下lambda的語法。

基礎語法

在lambda中我們遵循如下的表達式來編寫:

1 expression = (variable) -> action
  • variable: 這是一個變量,一個占位符。像x,y,z,可以是多個變量;
  • action: 這裏我稱它為action, 這是我們實現的代碼邏輯部分,它可以是一行代碼也可以是一個代碼片段。

可以看到Java中lambda表達式的格式:參數、箭頭、以及動作實現,當一個動作實現無法用一行代碼完成,可以編寫 一段代碼用{}包裹起來。

lambda表達式可以包含多個參數,例如:

1 int sum = (x, y) -> x + y;

這時候我們應該思考這段代碼不是之前的x和y數字相加,而是創建了一個函數,用來計算兩個操作數的和。 後面用int類型進行接收,在lambda中為我們省略去了return。

函數式接口

函數式接口是只有一個方法的接口,用作lambda表達式的類型。前面寫的例子就是一個函數式接口,來看看jdk中的Runnable源碼

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object‘s * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }

這裏只有一個抽象方法run,實際上你不寫public abstract也是可以的,在接口中定義的方法都是public abstract的。 同時也使用註解@FunctionalInterface告訴編譯器這是一個函數式接口,當然你不這麽寫也可以,標識後明確了這個函數中 只有一個抽象方法,當你嘗試在接口中編寫多個方法的時候編譯器將不允許這麽幹。

嘗試函數式接口

我們來編寫一個函數式接口,輸入一個年齡,判斷這個人是否是成人。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class FunctionInterfaceDemo { @FunctionalInterface interface Predicate<T> { boolean test(T t); } /** * 執行Predicate判斷 * * @param age 年齡 * @param predicate Predicate函數式接口 * @return 返回布爾類型結果 */ public static boolean doPredicate(int age, Predicate<Integer> predicate) { return predicate.test(age); } public static void main(String[] args) { boolean isAdult = doPredicate(20, x -> x >= 18); System.out.println(isAdult); } }

從這個例子我們很輕松的完成 是否是成人 的動作,其次判斷是否是成人,在此之前我們的做法一般是編寫一個 判斷是否是成人的方法,是無法將 判斷 共用的。而在本例只,你要做的是將 行為 (判斷是否是成人,或者是判斷是否大於30歲) 傳遞進去,函數式接口告訴你結果是什麽。

實際上諸如上述例子中的接口,偉大的jdk設計者為我們準備了java.util.function包

技術分享圖片

我們前面寫的Predicate函數式接口也是JDK種的一個實現,他們大致分為以下幾類:

技術分享圖片

消費型接口示例

1 2 3 4 5 6 public static void donation(Integer money, Consumer<Integer> consumer){ consumer.accept(money); } public static void main(String[] args) { donation(1000, money -> System.out.println("好心的麥樂迪為Blade捐贈了"+money+"元")) ; }

供給型接口示例

1 2 3 4 5 6 7 8 9 10 public static List<Integer> supply(Integer num, Supplier<Integer> supplier){ List<Integer> resultList = new ArrayList<Integer>() ; for(int x=0;x<num;x++) resultList.add(supplier.get()); return resultList ; } public static void main(String[] args) { List<Integer> list = supply(10,() -> (int)(Math.random()*100)); list.forEach(System.out::println); }

函數型接口示例

轉換字符串為Integer

1 2 3 4 5 6 public static Integer convert(String str, Function<String, Integer> function) { return function.apply(str); } public static void main(String[] args) { Integer value = convert("28", x -> Integer.parseInt(x)); }

斷言型接口示例

篩選出只有2個字的水果

1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static List<String> filter(List<String> fruit, Predicate<String> predicate){ List<String> f = new ArrayList<>(); for (String s : fruit) { if(predicate.test(s)){ f.add(s); } } return f; } public static void main(String[] args) { List<String> fruit = Arrays.asList("香蕉", "哈密瓜", "榴蓮", "火龍果", "水蜜桃"); List<String> newFruit = filter(fruit, (f) -> f.length() == 2); System.out.println(newFruit); }

默認方法

在Java語言中,一個接口中定義的方法必須由實現類提供實現。但是當接口中加入新的API時, 實現類按照約定也要修改實現,而Java8的API對現有接口也添加了很多方法,比如List接口中添加了sort方法。 如果按照之前的做法,那麽所有的實現類都要實現sort方法,JDK的編寫者們一定非常抓狂。

幸運的是我們使用了Java8,這一問題將得到很好的解決,在Java8種引入新的機制,支持在接口中聲明方法同時提供實現。 這令人激動不已,你有兩種方式完成 1.在接口內聲明靜態方法 2.指定一個默認方法。

我們來看看在JDK8中上述List接口添加方法的問題是如何解決的

1 2 3 4 5 6 7 8 9 default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } }

翻閱List接口的源碼,其中加入一個默認方法default void sort(Comparator<? super E> c)。 在返回值之前加入default關鍵字,有了這個方法我們可以直接調用sort方法進行排序。

1 2 3 List<Integer> list = Arrays.asList(2, 7, 3, 1, 8, 6, 4); list.sort(Comparator.naturalOrder()); System.out.println(list);

Comparator.naturalOrder()是一個自然排序的實現,這裏可以自定義排序方案。你經常看到使用Java8操作集合的時候可以直接foreach的原因也是在Iterable接口中也新增了一個默認方法:forEach,該方法功能和 for 循環類似,但是允許 用戶使用一個Lambda表達式作為循環體。

例子:

public class LambdaTest {


public static void main(String[] arg){

int num = Runtime.getRuntime().availableProcessors();
System.out.println("處理器數: " + num);

//1.用lambda表達式實現Runnable
// Java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8, too much code for too little to do");
}
}).start();
//Java 8方式:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
new Thread( () -> {
System.out.println("In Java8, Lambda expression rocks !!");
}).start();


//2.使用lambda表達式對列表進行叠代
String[] atp = {"Rafael Nadal", "Novak Djokovic",
"Stanislas Wawrinka",
"David Ferrer","Roger Federer",
"Andy Murray","Tomas Berdych",
"Juan Martin Del Potro"};
List<String> players = Arrays.asList(atp);
// 以前的循環方式
for (String player : players) {
System.out.print(player + "; ");
}

// 使用 lambda 表達式以及函數操作(functional operation)
players.forEach((player) -> System.out.print(player + "; "));
// 在 Java 8 中使用雙冒號操作符(double colon operator)
players.forEach(System.out::println);


//3.使用Lambdas排序集合
//使用匿名內部類進行排序
// players.sort(new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return (o1.compareTo(o2));
// }
// });
// System.out.println("排序後: ");
// players.forEach((player) -> System.out.print(player + "; "));
//使用Lambda
Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
Collections.sort(players, sortByName);
System.out.println("排序後: ");
players.forEach((player) -> System.out.print(player + "; "));


//使用lambda表達式和函數式接口Predicate
System.out.println("測試Predicate");
testPredicate();



}


public static void testPredicate(){
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));
}
};


System.out.println("所有java程序員的姓名:");
javaProgrammers.forEach((p) ->System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

System.out.println("所有php程序員的姓名:");
phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));


//Consumer是java8的函數式接口
System.out.println("給程序員加薪 5% :");
Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
javaProgrammers.forEach(giveRaise);
phpProgrammers.forEach(giveRaise);



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

// 重用filters
System.out.println("年齡大於 24歲的女性 Java programmers:");
javaProgrammers.stream()
.filter(ageFilter)
.filter(genderFilter)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));


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


//排序
System.out.println("根據 name 排序,並顯示前5個 Java programmers:");
List<Person> sortedJavaProgrammers = javaProgrammers
.stream()
.sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
.limit(5)
.collect(Collectors.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(Collectors.toList());

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


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




System.out.println("將 PHP programmers 的 first name 拼接成字符串:");
String phpDevelopers = phpProgrammers
.stream()
.map(Person::getFirstName)
.collect(Collectors.joining(" ; ")); // 在進一步的操作中可以作為標記(token)
System.out.println(phpDevelopers);

System.out.println("將 Java programmers 的 first name 存放到 Set:");
Set<String> javaDevFirstName = javaProgrammers
.stream()
.map(Person::getFirstName)
.collect(Collectors.toSet());

System.out.println("將 Java programmers 的 first name 存放到 TreeSet:");
TreeSet<String> javaDevLastName = javaProgrammers
.stream()
.map(Person::getLastName)
.collect(Collectors.toCollection(TreeSet::new));


//Streams 還可以是並行的(parallel). Stream是對集合的包裝
System.out.println("計算付給 Java programmers 的所有money:");
int totalSalary = javaProgrammers
.parallelStream()
.mapToInt(p -> p.getSalary())
.sum();

//使用summaryStatistics方法獲得stream 中元素的各種匯總數據。
// 接下來,我們可以訪問這些方法,比如getMax, getMin, getSum或getAverage:
//計算 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());
}
}
public class Person {

private String firstName, lastName, job, gender;
private int salary, age;

public Person(String firstName, String lastName, String job,
String gender, int age, int salary) {
this.firstName = firstName;
this.lastName = lastName;
this.gender = gender;
this.age = age;
this.job = job;
this.salary = 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;
}
}

Java8新特性--Lambda表達式