Java8特性總結——恕我直言你可能真的不會java系列學習筆記
Java8特性總結
目錄- Java8特性總結
——恕我直言你可能真的不會java系列學習筆記
字母哥講:恕我直言你可能真的不會java系列的學習筆記,以下是學習資料直達地址
視訊:https://www.bilibili.com/video/BV1sE411P7C1
文章:https://www.kancloud.cn/hanxt/javacrazy/1568811
主講: java8基礎特性
恕我直言你可能真的不會java系列-lambda、streamAPI、文字塊等特性深入講解
學習筆記原始碼:https://github.com/ManMian/lambda-StreamAPI
01.lambda表示式會用了麼
lambda表示式表達介面函式的實現
一、介面定義
1、經典OOP的實現樣式
public class LambdaDemo { //函式定義 public void printSomething(String something) { System.out.println(something); } //通過建立物件呼叫函式 public static void main(String[] args) { LambdaDemo demo = new LambdaDemo(); String something = "I am learning Lambda"; demo.printSomething(something); } }
2、建立功能介面,並對該介面定義抽象方法
public class LambdaDemo {
//抽象功能介面
interface Printer {
void print(String val);
}
//通過引數傳遞功能介面
public void printSomething(String something, Printer printer) {
printer.print(something);
}
}
二、傳統的介面函式實現方式
public static void main(String[] args) {
LambdaDemo demo = new LambdaDemo();
String something = "I am using a Functional interface";
//實現Printer介面
Printer printer = new Printer() {
@Override
public void print(String val) {
//控制檯列印
System.out.println(val);
}
};
demo.printSomething(something, printer);
}
三、lambda表示式實現方式
lambda表示式的語法:
(param1,param2,param3 ...,paramN)- > { //程式碼塊; }
首先我們知道lambda表示式,表達的是介面函式
箭頭左側是函式的逗號分隔的形式引數列表
箭頭右側是函式體程式碼
public static void main(String[] args) {
LambdaDemo demo = new LambdaDemo();
String something = "I am learning Lambda";
//實現Printer介面(請關注下面這行lambda表示式程式碼)
Printer printer = (String toPrint)->{System.out.println(toPrint);};
//呼叫介面列印
demo.printSomething(something, printer);
}
例子
package main.java;
public class LambdaDemo1 {
interface Printer{
void printer(String val);
}
public void pringSomething(String something,Printer printer){
printer.printer(something);
}
public static void main(String[] args) {
LambdaDemo1 lambdaDemo1 = new LambdaDemo1();
String some = "asdasasa";
//不使用lambda表示式
Printer printer = new Printer() {
@Override
public void printer(String val) {
System.out.println(val);
}
};
lambdaDemo1.pringSomething(some,printer);
/*1、使用lambda表示式
*介面匿名實現類,簡化函式
* ①引數
* ②函式體
* */
Printer printer1 = (String val) ->{
System.out.println(val);
};
//1.1、進一步簡化,去掉引數型別
Printer printer2 = (val) ->{
System.out.println(val);
};
//1.2、進一步簡化,去掉引數括號(前提:只有一個引數)
Printer printer3 = val ->{
System.out.println(val);
};
//1.3、進一步簡化,去掉函式體大括號(前提:函式體只有一行程式碼)
Printer printer4 = val -> System.out.println(val);
//1.4、最後可精簡為如下,會自動推斷lambda傳入的引數型別
lambdaDemo1.pringSomething(some,val -> System.out.println(val));
//1.5、無引數傳入的話,可以簡寫成
// () -> System.out.println("無參");
/*
* 總結:
* 省略型別:自動推斷
* 省略括號:一個引數
*
* lambda表示式表達的是介面函式,
* 箭頭左側是函式引數,箭頭右側是函式體。
* 函式的引數型別和返回值型別都可以省略,程式會根據介面定義的上下文自動確定資料型別。
* */
}
}
02.Stream代替for迴圈(初識Stream-API)
認識
Java Stream就是一個數據流經的管道,並且在管道中對資料進行操作,然後流入下一個管道。
管道的功能包括:Filter(過濾)、Map(對映)、sort(排序)等,集合資料通過Java Stream管道處理之後,轉化為另一組集合或資料輸出。
例子 Stream代替for迴圈
package main.java;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamDemo1 {
public static void main(String[] args) {
List<String> letters = Arrays.asList("e", "c", "cbn", "zxc", "caz", "d", "b", "a");
//不使用StreamAPI
/* for (String letter : letters) {
//轉換大寫
String temp = letter.toLowerCase();
//排序
//轉
//總之非常麻煩
}*/
//1、集合 使用StreamAPI
//陣列轉換成流
List<String> lettertsNew = letters.stream()
//過濾:找出以c開頭的元素
.filter(s -> s.startsWith("c"))
//將過濾後的結果全部大寫,其中::為函式引用
.map(String::toUpperCase)
//排序,可寫排序規則
.sorted()
//將流轉換成集合
.collect(Collectors.toList());
System.out.println(lettertsNew);
//2、陣列轉換成流
String[] letters2 = {"e", "c", "cbn", "zxc", "caz", "d", "b", "a"};
Stream.of(letters2).filter(s -> s.startsWith("c")).map(String::toUpperCase).sorted().toArray(String[]::new);
//3、set轉成流:集合類是一樣的
Set<String> lettersSet = new HashSet<>(letters);
List<String> lettertsNewSet = lettersSet.stream()
//過濾:找出以c開頭的元素
.filter(s -> s.startsWith("c"))
//將過濾後的結果全部大寫,其中::為函式引用
.map(String::toUpperCase)
//排序,可寫排序規則
.sorted()
//將流轉換成集合
.collect(Collectors.toList());
//4、文字檔案轉換成流
Paths.get("file.text");
try {
Stream<String> stringStream = Files.lines(Paths.get("file.text"));
List<String> f = stringStream
//過濾:找出以c開頭的元素
.filter(s -> s.startsWith("c"))
//將過濾後的結果全部大寫,其中::為函式引用
.map(String::toUpperCase)
//排序,可寫排序規則
.sorted()
//將流轉換成集合
.collect(Collectors.toList());
} catch (IOException e) {
e.printStackTrace();
}
}
}
題外話 介紹雙冒號操作符
雙冒號運算就是Java中的[方法引用] Method
[方法引用]的格式是
類名::方法名
例如
1.表示式:
person -> person.getName();
可以替換成:
Person::getName
2.表示式:
() -> new HashMap<>();
可以替換成:
HashMap::new
方法引用的種類
方法引用有四種,分別是:
指向靜態方法的引用
指向某個物件的例項方法的引用
指向某個型別的例項方法的引用
指向構造方法的引用
其實,JVM 本身並不支援指向方法引用,過去不支援,現在也不支援。
Java 8 對方法引用的支援只是編譯器層面的支援,虛擬機器執行引擎並不瞭解方法引用。編譯器遇到方法引用的時候,會像上面那樣自動推斷出程式設計師的意圖,將方法引用還原成介面實現物件,或者更形象地說,就是把方法引用設法包裝成一個介面實現物件,這樣虛擬機器就可以無差別地執行位元組碼檔案而不需要管
什麼是方法引用了。
需要注意的是,方法引用是用來簡化介面實現程式碼的,並且凡是能夠用方法引用來簡化的介面,都有這樣的特徵:有且只有一個待實現的方法。這種介面在 Java 中有個專門的名稱: 函式式介面。當你用試圖用方法引用替代一個非函式式介面時,會有這樣的錯誤提示: xxx is not a functional interface。
3.Stream的filter與謂語邏輯
什麼是謂詞邏輯?
WHERE 和 AND 限定了主語employee是什麼,那麼WHERE和AND語句所代表的邏輯就是謂詞邏輯
SELECT *
FROM employee
WHERE age > 70
AND gender = 'M'
謂詞邏輯的複用
filter函式中lambda表示式為一次性使用的謂詞邏輯。如果我們的謂詞邏輯需要被多處、多場景、多程式碼中使用,通常將它抽取出來單獨定義到它所限定的主語實體中。
比如:將下面的謂詞邏輯定義在Employee實體class中。
public static Predicate<Employee> ageGreaterThan70 = x -> x.getAge() >70;
public static Predicate<Employee> genderM = x -> x.getGender().equals("M");
and語法(並集)
or語法(交集)
negate語法(取反)
例子
package model;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.function.Predicate;
@Data
@AllArgsConstructor
//員工
public class Employee {
private Integer id;
private Integer age; //年齡
private String gender; //性別
private String firstName; //名字
private String lastName; //姓氏
//Predicate介面,在英語中這個單詞的意思是:謂詞
//謂詞邏輯的複用如下
//e -> e.getAge() > 70 && e.getGender().equals("M")
public static Predicate<Employee> ageGreaterThan70 = x -> x.getAge() >70;
public static Predicate<Employee> genderM = x -> x.getGender().equals("M");
}
package model;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFilterPredicate {
public static void main(String[] args){
Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
Employee e2 = new Employee(2,13,"F","Martina","Hengis");
Employee e3 = new Employee(3,43,"M","Ricky","Martin");
Employee e4 = new Employee(4,26,"M","Jon","Lowman");
Employee e5 = new Employee(5,19,"F","Cristine","Maria");
Employee e6 = new Employee(6,15,"M","David","Feezor");
Employee e7 = new Employee(7,68,"F","Melissa","Roy");
Employee e8 = new Employee(8,79,"M","Alex","Gussin");
Employee e9 = new Employee(9,15,"F","Neetu","Singh");
Employee e10 = new Employee(10,45,"M","Naveen","Jain");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
List<Employee> filtered = employees.stream()
//filter(寫謂詞邏輯) 年齡大於70 並且是男性
.filter(e -> e.getAge() > 70 && e.getGender().equals("M"))
.collect(Collectors.toList());
//使用可複用謂詞邏輯
List<Employee> filteredAnd = employees.stream()
.filter(Employee.ageGreaterThan70
.and(Employee.genderM))
.collect(Collectors.toList());
//or
List<Employee> filteredOr = employees.stream()
.filter(Employee.ageGreaterThan70
.or(Employee.genderM))
.collect(Collectors.toList());
//negate 取反
List<Employee> filteredNegate = employees.stream()
.filter(
Employee.ageGreaterThan70
.or(Employee.genderM)
.negate()
)
.collect(Collectors.toList());
System.out.println("filtered="+filtered);
System.out.println("filteredAnd="+filteredAnd);
System.out.println("filteredOr="+filteredOr);
System.out.println("filteredNegate="+filteredNegate);
}
}
4.Stream的map資料轉換(Stream管道流的map操作)
map函式的作用就是針對管道流中的每一個數據元素進行轉換操作。
例子
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamMap1 {
public static void main(String[] args) {
List<String> alpha = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");
//不使用Stream管道流
List<String> alphaUpper = new ArrayList<>();
for (String s : alpha) {
alphaUpper.add(s.toUpperCase());
}
System.out.println(alphaUpper); //[MONKEY, LION, GIRAFFE, LEMUR]
// 1、Stream管道流map的基礎用法:使用Stream管道流
List<String> collect = alpha.stream().map(String::toUpperCase).collect(Collectors.toList());
//上面使用了方法引用,和下面的lambda表示式語法效果是一樣的
//List<String> collect = alpha.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());
System.out.println(collect); //[MONKEY, LION, GIRAFFE, LEMUR]
//2、處理非字串型別集合元素
List<Integer> lengths = alpha.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(lengths); //[6, 4, 7, 5]
Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
//除了mapToInt。還有maoToLong,mapToDouble等等用法
.mapToInt(String::length)
.forEach(System.out::println);
}
}
import model.Employee;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamMap2 {
public static void main(String[] args) {
Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
Employee e2 = new Employee(2,13,"F","Martina","Hengis");
Employee e3 = new Employee(3,43,"M","Ricky","Martin");
Employee e4 = new Employee(4,26,"M","Jon","Lowman");
Employee e5 = new Employee(5,19,"F","Cristine","Maria");
Employee e6 = new Employee(6,15,"M","David","Feezor");
Employee e7 = new Employee(7,68,"F","Melissa","Roy");
Employee e8 = new Employee(8,79,"M","Alex","Gussin");
Employee e9 = new Employee(9,15,"F","Neetu","Singh");
Employee e10 = new Employee(10,45,"M","Naveen","Jain");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
//每人漲一歲,性別換全詞
/*List<Employee> maped = employees.stream()
.map(e -> {
e.setAge(e.getAge() + 1);
e.setGender(e.getGender().equals("M")?"male":"female");
return e;
}).collect(Collectors.toList());*/
List<Employee> maped = employees.stream()
.peek(e -> {
e.setAge(e.getAge() + 1);
e.setGender(e.getGender().equals("M")?"male":"female");
}).collect(Collectors.toList());
System.out.println(maped);
}
}
import java.util.Arrays;
import java.util.List;
public class StreamFlatMap {
public static void main(String[] args) {
List<String> words = Arrays.asList("hello", "word");
words.stream()
.map(w -> Arrays.stream(w.split(""))) //[[h,e,l,l,o],[w,o,r,l,d]]
.forEach(System.out::println);
//flatMap可以理解為將若干個子管道中的資料全都,平面展開到父管道中進行處理
words.stream()
.flatMap(w -> Arrays.stream(w.split(""))) // [h,e,l,l,o,w,o,r,l,d]
.forEach(System.out::println);
}
}
5.Stream的狀態與並行操作
Stream管道流的基本操作
- 源操作:可以將陣列、集合類、行文字檔案轉換成管道流Stream進行資料處理
- 中間操作:對Stream流中的資料進行處理,比如:過濾、資料轉換等等
- 終端操作:作用就是將Stream管道流轉換為其他的資料型別。
中間操作:有狀態與無狀態
狀態通常代表公用資料,有狀態就是有“公用資料”
Limit與Skip管道資料擷取
Distinct元素去重
Sorted排序
序列、並行與順序
例子
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamState {
public static void main(String[] args) {
//1、Limit與Skip管道資料擷取
List<String> limitN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
.limit(2)
.collect(Collectors.toList());
List<String> skipN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
.skip(2)
.collect(Collectors.toList());
//2、Distinct元素去重
List<String> uniqueAnimals = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.distinct()
.collect(Collectors.toList());
//3、Sorted排序
List<String> alphabeticOrder = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
.sorted()
.collect(Collectors.toList());
//4、序列、並行與順序
//預設序列
//並行操作parallel,操作無狀態操作
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.parallel()
.forEach(System.out::println);
}
}
結果
Giraffe
Lion
Lemur
Lion
Monkey
Process finished with exit code 0
6.Stream效能差問號 不要人云亦云
7.像使用SQL一樣排序集合
一、字串List排序
二、整數型別List排序
三、按物件欄位對List