Java 8 新特性(二)Lambda表達式

一、Lambda表達式簡介

個人理解,lambda表達式就是一種新的語法,沒有什麼新奇的,簡化了開發者的編碼,其實底層還是一些常規的代碼。Lambda 是一個匿名函數,我們可以把 Lambda 表達式理解爲是一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)。可以寫出更簡潔、更靈活的代碼。作爲一種更緊湊的代碼風格,使Java的語言表達能力得到了提升。

  • Lambda表達式本質只是一顆讓編程人員更加得心應手的“語法糖”,它只存在於Java源代碼中,由編譯器把它轉換爲常規的Java類代碼。
  • Lambda表達式有點類似於方法,由參數列表和一個使用這些參數的主體(可以是一個表達式或一個代碼塊)組成
  • Lambda表達式與 Stream API 聯合使用,可以方便的操縱集合,完成對集合中元素的排序和過濾等操作

Lambda表達式的語法

Lambda 表達式的基礎語法:Java8中引入了一個新的操作符  "- >"  該操作符稱爲箭頭操作符或 Lambda 操作符
箭頭操作符將 Lambda 表達式拆分成兩部分:
左側:Lambda 表達式的參數列表
右側:Lambda 表達式中所需執行的功能, 即 Lambda 體

new Thread( ()  ->  System.out.println("thread"));

針對這種實行,我們怎麼理解呢?其實很簡單,上看一下上述lambda表達式的語法:() -> {}:

括號就是接口方法的括號,接口方法如果有參數,也需要寫參數。只有一個參數時,括號可以省略。-> : 分割左右部分的,沒啥好講的。{} : 要實現的方法體。只有一行代碼時,可以不加括號,可以不寫return。

(parameters) -> expression

(parameters) ->{ statements; }

以下是lambda表達式的重要特徵:

  • 可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。
  • 可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。
  • 可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
  • 可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,大括號需要指定明表達式返回了一個數值。

案例

	@Test
	public void test1(){
		String[] data = {"marvin","jerry","sally","max","jason"};
		List<String> names = Arrays.asList(data);
		
		System.out.println("方式一:傳統的遍歷集合方式");
		for(String name : names){
			System.out.println(name);
		}
		
		System.out.println("方式二:使用Lambda表達式");
		names.forEach((name) -> System.out.println(name));
		
		System.out.println("方式三:使用Lambda表達式");
		names.forEach(System.out::println);
	}

解讀:forEach 方法的參數是 Consumer 接口

public void forEach(Consumer<? super E> action)

 Consumer 接口中只有一個實現方法 void accept(T t);

而 names.forEach((name) -> System.out.println(name));相當於實現了accept方法

左邊

name:是入參

右邊

System.out.println(name) :是accept方法的實現

 

二、Lambda表達式中的方法引用

編譯器能根據上下文來推斷Lambda表達式的參數的場合時,可以在Lambda表達式中省略參數,直接通過 “::” 符號來引用方法

方法引用的語法格式有以下三種:

  • 第一種:objectName :: instanceMethod //對象引用實例方法
  • 第二種:ClassName :: staticMethod //類引用靜態方法
  • 第三種:ClassName :: InstanceMethod //類引用實例方法

以下兩種Lambda表達式是等價的

names.forEach((name)->System.out.println(name));
等同於:names.forEach(System.out::println);

x->System.out.println(x)

等同於:System.out :: println //引用實例方法

(x,y)->Math.max(x,y)

等同於:Math :: max //引用靜態方法

x->x.toLowerCase()

等同於:String :: toLowerCase //引用實例方法

 

三、函數式接口(FunctionalInterface)

在JDK8中,定義了一個 @FunctionalInterface 註解

 

Lambda表達式 只能賦值給 聲明爲 函數式接口 的Java類型的 變量

如:Consumer、Runnable、Comparator、Predicate接口都標註爲函數式接口,因此可以接收Lambda表達式

合法的賦值:Runnable race = () -> System.out.println("Hello world !");

非法的賦值:String str = () -> {return "hello".toUpperCase();};

因爲String類沒有標註爲函數式接口,不能接收Lambda表達式,會導致編譯錯誤 

 

四、Stream API

Java 8 API添加了一個新的抽象稱爲流Stream,可以讓你以一種聲明的方式處理數據。 

這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。

元素流在管道中經過中間操作(intermediate operation)的處理,最後由最終操作(terminal operation)得到前面處理的結果。

生成流

在 Java 8 中, 集合接口有兩個方法來生成流:

  • stream() − 爲集合創建串行流

  • parallelStream() − 爲集合創建並行流

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

操作集合的方法 :Collection.stream(); 會返回一個Stream對象,通過這個Stream對象,以流的方式操作集合中的元素 

 

1、filter

Stream<T> filter(Predicate<? super T> predicate); 

對集合中的元素進行過濾,返回包含符合條件的元素流

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 獲取空字符串的數量
long count = strings.stream().filter(string -> string.isEmpty()).count();

 

2、forEach

void forEach(Consumer<? super T> action);

遍歷集合中的元素

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

strings.stream().forEach(string -> System.out.println(string));
strings.stream().forEach(System.out::println);//是上面的簡寫方式

 

3、map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

map 方法用於映射每個元素到對應的結果

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 獲取對應的平方數
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());

案例:先對集合中元素進行操作,再對集合中年齡大於27歲的進行過濾,並遍歷集合

		List<Person> persons = new ArrayList<Person>(){
			{//匿名類初始化參數
				add(new Person("marvin", 18));
				add(new Person("jerry", 31));
				add(new Person("sally", 30));
				add(new Person("max", 27));
				add(new Person("jason", 32));

			}
		};

		persons.stream()
				//年齡大於27歲的對象返回,其他的設置爲null
				.map(e ->{
					if(e.getAge()>27){
						return e;
					}
					return null;
				})
				//過濾掉null的數據
				.filter(e -> e!=null)
				//將流轉換爲list集合
				.collect(Collectors.toList())
				//遍歷集合中的對象
				.forEach(System.out::println);

 

 4、limit

Stream<T> limit(long maxSize);

limit 方法用於獲取指定數量的流,返回參數maxSize所指定的個數的元素

//以下代碼片段使用 limit 方法打印出隨機 10 條數據
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
//獲取前三個
List<String> s = Arrays.asList("1","2","3","4","5","6");
s.stream().limit(3).forEach(System.out::println);

 

5、sorted

Stream<T> sorted();

sorted 方法用於對流中的元素自然排序

Stream<T> sorted(Comparator<? super T> comparator);

根據參數指定的比較規則,對集合中元素排序

List<String> s = Arrays.asList("6","5","3","1","2","1");
s.stream().sorted().forEach(System.out::println);

案例:將集合中對象以年齡倒序排序 

List<Person> persons = new ArrayList<Person>(){
			{//匿名類初始化參數
				add(new Person("marvin", 18));
				add(new Person("jerry", 31));
				add(new Person("sally", 30));
				add(new Person("max", 27));
				add(new Person("jason", 32));

			}
		};
		persons.stream().sorted(Comparator.comparing(Person::getAge).reversed()).forEach(System.out::println);

 

6、Collectors

Collectors 類實現了很多歸約操作,例如將流轉換成集合和聚合元素。Collectors 可用於返回列表或字符串:

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
 
System.out.println("篩選列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合併字符串: " + mergedString);

 

 

 

練習案例:

案例1:

對persons集合過濾,過濾條件爲姓名字符串的長度大於3,接着按照姓名排序,再把集合中前三個Person對象的信息打印出來

	public static void main(String[] args) {
		List<Person> persons = new ArrayList<Person>(){
			{//匿名類初始化參數
				add(new Person("marvin", 18));
				add(new Person("jerry", 31));
				add(new Person("sally", 30));
				add(new Person("max", 27));
				add(new Person("jason", 32));
				
			}
		};
		
		persons.stream().filter(p->p.getName().length()>3)//姓名字符串長度大於3
		.sorted((p1,p2)->(p1.getName().compareTo(p2.getName())))
		.limit(3)//取出 三個元素
		.forEach(p->System.out.println(p.getName()+":"+p.getAge()));
	}

 

案例2:使用List流排序

package com.stream.demo;
 
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
 
public class StreamListDemo {
	public static void main(String[] args) {
		List<Student> list = new ArrayList<>();
		list.add(new Student(1, "Mahesh", 12));
		list.add(new Student(2, "Suresh", 15));
		list.add(new Student(3, "Nilesh", 10));
 
		System.out.println("---Natural Sorting by Name---");
		List<Student> slist = list.stream().sorted().collect(Collectors.toList());
		slist.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
 
		System.out.println("---Natural Sorting by Name in reverse order---");
		slist = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
		slist.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
 
		System.out.println("---Sorting using Comparator by Age---");
		slist = list.stream().sorted(Comparator.comparing(Student::getAge)).collect(Collectors.toList());
		slist.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
 
		System.out.println("---Sorting using Comparator by Age with reverse order---");
		slist = list.stream().sorted(Comparator.comparing(Student::getAge).reversed()).collect(Collectors.toList());
		slist.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
	}
}
package com.stream.demo;
 
public class Student implements Comparable<Student> {
	private int id;
	private String name;
	private int age;
 
	public Student(int id, String name, int age) {
		this.id = id;
		this.name = name;
		this.age = age;
	}
 
	public int getId() {
		return id;
	}
 
	public String getName() {
		return name;
	}
 
	public int getAge() {
		return age;
	}
 
	@Override
	public int compareTo(Student ob) {
		return name.compareTo(ob.getName());
	}
 
	@Override
	public boolean equals(final Object obj) {
		if (obj == null) {
			return false;
		}
		final Student std = (Student) obj;
		if (this == std) {
			return true;
		} else {
			return (this.name.equals(std.name) && (this.age == std.age));
		}
	}
 
	@Override
	public int hashCode() {
		int hashno = 7;
		hashno = 13 * hashno + (name == null ? 0 : name.hashCode());
		return hashno;
	}
}

執行結果

---Natural Sorting by Name---
Id:1, Name: Mahesh, Age:12
Id:3, Name: Nilesh, Age:10
Id:2, Name: Suresh, Age:15
---Natural Sorting by Name in reverse order---
Id:2, Name: Suresh, Age:15
Id:3, Name: Nilesh, Age:10
Id:1, Name: Mahesh, Age:12
---Sorting using Comparator by Age---
Id:3, Name: Nilesh, Age:10
Id:1, Name: Mahesh, Age:12
Id:2, Name: Suresh, Age:15
---Sorting using Comparator by Age with reverse order---
Id:2, Name: Suresh, Age:15
Id:1, Name: Mahesh, Age:12
Id:3, Name: Nilesh, Age:10

 

案例3:使用set流排序


package com.stream.demo;
 
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
 
public class StreamSetDemo {
	public static void main(String[] args) {
		Set<Student> set = new HashSet<>();
		set.add(new Student(1, "Mahesh", 12));
		set.add(new Student(2, "Suresh", 15));
		set.add(new Student(3, "Nilesh", 10));
 
		System.out.println("---Natural Sorting by Name---");
		System.out.println("---Natural Sorting by Name---");
		set.stream().sorted().forEach(e -> System.out.println("Id:"
						+ e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
 
		System.out.println("---Natural Sorting by Name in reverse order---");
		set.stream().sorted(Comparator.reverseOrder()).forEach(e -> System.out.println("Id:"
						+ e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
 
		System.out.println("---Sorting using Comparator by Age---");
		set.stream().sorted(Comparator.comparing(Student::getAge))
						.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
 
		System.out.println("---Sorting using Comparator by Age in reverse order---");
		set.stream().sorted(Comparator.comparing(Student::getAge).reversed())
						.forEach(e -> System.out.println("Id:" + e.getId() + ", Name: " + e.getName() + ", Age:" + e.getAge()));
	}
}

案例4:使用Map流排序

package com.stream.demo;
 
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
 
public class StreamMapDemo {
	public static void main(String[] args) {
		Map<Integer, String> map = new HashMap<>();
		map.put(15, "Mahesh");
		map.put(10, "Suresh");
		map.put(30, "Nilesh");
 
		System.out.println("---Sort by Map Value---");
		map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getValue))
						.forEach(e -> System.out.println("Key: "+ e.getKey() +", Value: "+ e.getValue()));
 
		System.out.println("---Sort by Map Key---");System.out.println("---Sort by Map Key---");
		map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey))
						.forEach(e -> System.out.println("Key: "+ e.getKey() +", Value: "+ e.getValue()));
	}
}

 

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