這篇文章主要介紹Java 8的Lambda表達式和Steam流的使用,以及其他特性。
目錄:
<a name="簡介"></a>
簡介
Java 8帶來的最大的改變的是添加了Lambda(λ)表達式,提供了新的API(稱為"流",Stream)其思路在和使用數(shù)據(jù)庫查詢語言中的思路類似-用更高級的方式表達想要的東西,其他的還有新的日期的API、GC的改進、并發(fā)改進等。
<a name="函數(shù)式編程"></a>
函數(shù)式編程
函數(shù)式編程是一種編程范式,它的主要思想是把函數(shù)過程盡量寫成一系列嵌套的函數(shù)調(diào)用。具有以下幾個特點
函數(shù)是一等公民:函數(shù)和其他數(shù)據(jù)類型一樣,可以賦值給其他變量,也可以作為參數(shù),傳遞給另外一個函數(shù),或者作為返回值返回。
沒有副作用,不修改狀態(tài)。
強調(diào)將計算過程分解成可復用的函數(shù)。
<a name="Lambda表達式"></a>
Lambda表達式
Lambda表達式?jīng)]有名字,但它有參數(shù)列表、函數(shù)主體、返回類型,可能還有一個可以拋出的異常列表。
使用案例 | Lambda示例 |
---|---|
布爾表達式 | (List<String> list) -> list.isEmpty(); |
創(chuàng)建對象 | () -> new User(); |
消費一個對象 | (User user) -> {System.out.println(user.getName());} |
從一個對象中選擇/抽取 | (String s) -> s.length(); |
組合兩個值 | (int a, int b) -> a * b; |
比較兩個對象 | (User user1, User user2) -> user1.getAge().compareTo(user2.getAge()); |
<a name="什么是函數(shù)式接口"></a>
什么是函數(shù)式接口
Lambda的設計者們?yōu)榱俗尙F(xiàn)有的功能與Lambda表達式良好兼容,于是產(chǎn)生了函數(shù)接口這個概念。函數(shù)式接口僅僅聲明了一個抽象方法的接口,這樣的接口可以隱式轉換為Lambda表達式,不過函數(shù)式接口可以包含默認方法和靜態(tài)方法。如下:
@FunctionalInterface
public interface FunctionInterface {
void test();
static void say() {
System.out.println("hello");
}
default void play() {
System.out.println("play");
}
}
使用:
FunctionInterface.say();
FunctionInterface functionInterface = () -> System.out.println("這是一個函數(shù)式接口");
functionInterface.test();
functionInterface.play();
輸出:
hello
這是一個函數(shù)式接口
play
<a name="Java8中重要的函數(shù)式接口"></a>
Java8中重要的函數(shù)式接口
下面是Java 8中一些常用的函數(shù)式接口。
接口名 | 參數(shù) | 返回類型 | 示例 |
---|---|---|---|
Predicate<T> | <T> | boolean | 判斷是否 |
Consumer<T> | <T> | void | 輸出一個值 |
Function<T> | <T> | R | 獲得一個對象的名字 |
Supplier<T> | None | T | 工廠方法 |
UnaryOperator<T> | T | T | 邏輯非(!) |
BinaryOperator<T> | (T, T) | T | 求兩個數(shù)的乘積(*) |
簡單的使用例子:
Predicate<Integer> predicate = x -> x == 1;
Consumer<Integer> consumer = x -> System.out.println(x);
Function<Integer, Integer> function = x -> x = x + 1;
Supplier<Integer> supplier = () -> 1;
UnaryOperator<Integer> unaryOperator = x -> x + 1;
BinaryOperator<Integer> binaryOperator = (x, y) -> x * y;
方法引用
方法引用是Lambda的一種快捷寫法,在某種情況下比Lambda更易讀,在使用方法引用時引用對象方法在分隔符::的前面,方法的名稱在后面。如下
(User user) -> user.getAge() == User::getAge
(str, i) -> str.substring(i) == String::substring
(String s) -> System.out.println(s) == System.out::println
構建方法引用的方式有三種:
- 指向靜態(tài)方法的方法引用, 例如:Objects::nonNull
- 指向任意類型實例方法的方法引用, 例如:String::length
- 指向現(xiàn)有對象的實例方法的方法引用 , 例如:user::getName
<a name="Stream流"></a>
Stream流
Stream是一個借口繼承了BaseStream接口,BaseStream接口繼承了AutoCloseable接口。它只能被消費一次,如果想要繼續(xù)使用,需要重新創(chuàng)建一個流。如下會拋出異常。
List<String> strs = Arrays.asList("A", "B", "C");
Stream<String> s = strs.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);
內(nèi)部迭代實現(xiàn)機制
外部迭代:首先調(diào)用Iterator方法,產(chǎn)生一個新的Iterator對象,進行控制迭代過程。
User jack = User.of("Jack", 21, "杭州");
User rose = User.of("Rose", 18, "杭州");
List<User> users = Arrays.asList(jack, rose);
for(User user : users) {
System.out.println(user.getName());
}
內(nèi)部迭代:首先調(diào)用stream方法,它的作用和iterator()方法一樣,不過它返回的是內(nèi)部迭代中相應的接口Stream。這樣做的好處就是在迭代時要進行多次操作時可以不用多次循環(huán),只需要迭代一次就好了。
像filter這種不產(chǎn)生新集合的方法叫做惰性求值方法,像count這樣最終會從Stream產(chǎn)生值的方法叫作及早求值方法。
例子:
User jack = User.of("Jack", 21, "杭州");
User rose = User.of("Rose", 18, "杭州");
List<User> users = Arrays.asList(jack, rose);
users.stream()
.filter(user -> {
System.out.println(user.getName());
return user.getAge() > 20;
});
System.out.println("------------");
users.stream()
.filter(user -> {
System.out.println(user.getName());
return user.getAge() > 20;
}).count();
輸出:
------------
Jack
Rose
結論:只有在需要的時候進行計算可以更好的提示效率
<a name="collect方法"></a>
常用的方法
collect方法
將Stream流轉換為一個集合
#輸入
Stream<String> stream = Stream.of("a", "b", "c");
List<String> strs = stream.collect(Collectors.toList());
strs.forEach(System.out::println);
#輸出
a
b
c
<a name="filter方法"></a>
filter方法
過濾掉流中不符合條件的元素
#輸入
Stream<Integer> nums = Stream.of(18, 19 ,20);
nums.filter(num -> num > 18).forEach(System.out::println);
#輸出
c
<a name="map方法"></a>
map方法
將流中的元素轉換為另外一個元素。
#輸入
Stream<Integer> nums = Stream.of(18, 19 ,20);
map(num -> num + 100).forEach(System.out::println);
#輸出
118
119
120
<a name="flatMap方法"></a>
flatMap方法
將底層的元素全部抽出來放到一起,如下將List中的元素全部抽取出來,流中只包含Integer元素。
List<Integer> nums1 = Arrays.asList(1, 2, 3);
List<Integer> nums2 = Arrays.asList(4, 6);
Stream.of(nums1, nums2).flatMap(num -> num.stream()).forEach(System.out::println);
<a name="reduce"></a>
reduce方法
將Stream流中的數(shù)據(jù)聚合成一個數(shù)據(jù)
#輸入
int ages = Stream.of(1, 2, 34).reduce(0, (a, b) -> a + b);
System.out.println(ages);
#輸出
37
<a name="創(chuàng)建無限流"></a>
創(chuàng)建無限流
Stream的API提供了兩個靜態(tài)方法創(chuàng)建無限流:iterate和generate。由兩個靜態(tài)方法創(chuàng)建的流會根據(jù)給定的函數(shù)按需創(chuàng)建流一般會使用limit限制流的大小。iterate方法會對每個新生成的值都調(diào)用函數(shù),generate方法不會對每個新生成的值應用函數(shù).
// 從0開始,生成10個偶數(shù)。
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
// 生成10個隨機數(shù)。
Stream.generate(Math::random). limit(10).forEach(System.out::println);
<a name="其他方法"></a>
其他方法
- skip:跳過指定個數(shù)的流。
- limit:返回不超過給定長度的流。
- max:找到最大的元素
- min:找到最小的元素
- findFirst:找到第一個匹配元素
- findAny:獲取任意一個元素
- anyMatch:是否存在一個匹配元素
- noneMatch:是否全部不匹配
- allMatch:是否全部匹配
- sorted:排序
- distinct:去重
<a name="高效Java8編程"></a>
其他
Optional
Optional是為了減少NullPointException,增加代碼的可讀性。Optional是一個final類型的類
Optional<User> optUser = Optional.of(user);
- empty:返回一個空的Optional實例。
- filter:如果滿足提交返回Optional對象,否則返回一個空的Optional對象。
- flatMap:如果值存在,就使用mapping函數(shù)調(diào)用,返回一個Optional對象,否則返回一個空對象。
- get:如果值存在返回用Optional封裝返回,否則拋出NoSuchElementException異常
- ifPresent:如果值存在,就返回該方法的調(diào)用,否則什么也不做。
- isPresent:如果值存在,就返回true,否則返回false。
- map:如果值存在,就執(zhí)行提供的mapping函數(shù)調(diào)用。
- of:如果值不存在就拋出異常,否則返回一個Optional封裝的對象。
- ofNullable:如果值為空就返回一個空的Optional對象,否則調(diào)用of方法。
- orElse:如果值存在就返回該值,否則返回默認值。
- orElseGet:如果值存在就返回該值,否則返回一個函數(shù)接口生成的值。
- orElseThrow:如果值存在就返回該值,否則拋出一個接口生成的異常。
DateTime
在Java 8 以前需要使用Date和SimpdateFormatter操作時間,而且都不是線程安全的,Date不僅包含日期還包含時間和毫秒數(shù),使用起來非常的困難,而Java 8把日期分成了LocalDate和LocalTime,還有LocalDateTime。
使用LocalDate操作日期
// 獲取當前日期
LocalDate now = LocalDate.now();
// 用靜態(tài)方法創(chuàng)建日期
LocalDate date = LocalDate.of(2017, 5, 1);
// 將String類型轉換為日期類型.注:02不能寫成2,否則會拋出DateTimeParseException
LocalDate endOfFeb = LocalDate.parse("2017-02-28");
// 獲取這個月的第一天的日期
now.with(TemporalAdjusters.firstDayOfMonth())
// 獲取這個月的最后一天
now.with(TemporalAdjusters.lastDayOfMonth())
使用LocalTime操作時間
// 獲取當前時間
LocalTime now = LocalTime.now();
// 舍棄納秒
now.withNano(0);
/*
* 獲取特定的時間
* Localtime.MIN 00:00
* Localtime.MIDNIGHT 00:00
* Localtime.NOON 12:00
* LocalTime.MAX 23:59:59.999999999
*/
now.with(LocalTime.MIN);
now.with(LocalTime.MIDNIGHT);
now.with(LocalTime.NOON);
now.with(LocalTime.MAX);
// of可以傳入四個參數(shù)
LocalTime zero = LocalTime.of(23, 59, 59, 999_999_999);
// parse方法可以轉換的格式有 HH:mm:ss.nnnnnnnnn HH:mm:ss HH:mm
LocalTime last = LocalTime.parse("23:59:59.999999999");
// 也可以自定義轉換格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
LocalTime start = LocalTime.parse("00:00:00", formatter);
使用LocalDateTime操作時間
// 獲取當前時間
LocalDateTime now = LocalDateTime.now();
// 獲取當前日期所在的月份、星期等
now.getDayOfMonth();
now.getDayOfWeek()
// 自定義
LocalDateTime last = LocalDateTime.parse("2017-05-09T23:25:58.700");
JDBC映射和新類型關聯(lián)
date -> LocalDate
time -> LocalTime
timestamp -> LocalDateTime
代碼地址
https://github.com/huangbuhuan/java8
<a name="參考鏈接"></a>