背景
接觸過Kotlin, RxJava蚂且, Java 8 Stream开镣, 越發(fā)對其中常用方法涉及的原理有點(diǎn)了解了殴蹄。
- forEach
- find
- filter
- map
- flatmap
知道他們的作用哗咆,也知道如何去使用蜘欲, 但是對其中的大概原理不是很明白益眉。最近熟悉Java8的Stream中提供的幾個常用方法晌柬。 試著自己用java方式實(shí)現(xiàn)了類似java8 Stream中的函數(shù)式方法。
接下來郭脂,會有3篇文章介紹年碘,如何不使用java 8 stream特性,來實(shí)現(xiàn)函數(shù)式常用方法, 通過這三篇文章展鸡,也能讓讀者理解其他函數(shù)式語言中的相關(guān)方法原理
- 用Java DIY 函數(shù)式方法—— forEach, find, filter
- 用Java DIY 函數(shù)式方法—— map
- 用Java DIY 函數(shù)式方法—— flatmap
注意
- 不適合對函數(shù)式一點(diǎn)基礎(chǔ)都沒有的讀者
- DIY實(shí)現(xiàn)不是完美的屿衅,僅僅是用實(shí)例表達(dá)函數(shù)式方法的理解
- 這個系列文章不是分析java 8 stream中的方法源碼,而是對java 8 stream特性莹弊,結(jié)合Kotlin涤久, Rxjava之類的理解, 使用純java的方式實(shí)現(xiàn)類似的函數(shù)式方法忍弛。
- 需要對java 中的泛型以及Collection有了解
- 會用到j(luò)ava 8 lambda表達(dá)式
- 要實(shí)際代碼驗(yàn)證响迂,需要 jdk 1.8
講解的模式如下:
- 給出某個場景
- 使用 java 8
- 使用DIY 函數(shù)實(shí)現(xiàn)
那就進(jìn)入主題吧: 用Java DIY 函數(shù)式方法—— forEach, find, filter
DIY 函數(shù)式方法
forEach
作用: forEach 遍歷集合中的每個元素,對每個元素做操作
/** 需求:
* 給定一個String集合细疚,然后遍歷蔗彤,打印出集合中每個元素
*/
我X, 這是哪門子需求疯兼,直接來個循環(huán)不就解決了么然遏?
大兄弟,莫發(fā)脾氣吧彪,靜下心來待侵,慢慢看下。
1. java 8 Iterable.forEach
java 8 在Iterable上添加了新方法 forEach姨裸, 同時也在Stream類中添加了forEach方法
List<String> list = Arrays.asList("Hello", "World!");
list.forEach(new Consumer<String>() {
@Override
public void accept(String item) {
out.println(item);
}
});
為什么這么寫秧倾,自己查看forEach方法的定義。使用lambda表達(dá)式啦扬,更簡潔
list.forEach(item -> out.println(item));
從上述可以簡單得到中狂,在java中 lambda表達(dá)式就是用來替代匿名接口實(shí)現(xiàn)的!
2. java 8 Stream.forEach
List<String> list = Arrays.asList("Hello", "World!");
list.stream().forEach(new Consumer<String>() {
@Override
public void accept(String item) {
out.print(item + " ");
}
});
使用lambda表達(dá)式扑毡,更簡潔
list.stream().forEach(item -> out.print(item + " "));
3. DIY forEach
簡單起見胃榕,這里使用函數(shù)形式,而不是java 8 stream那樣單獨(dú)類,使用鏈?zhǔn)秸{(diào)用勋又。
再看一次
forEach原理: 遍歷集合中的每一個元素苦掘,然后對其進(jìn)行我們想要的操作
注意其中 2點(diǎn):
- 遍歷
- 對每個item都有操作
所以,需要把forEach方法設(shè)計為
public static <T> void forEach(Collection<T> collection, Action<T> action){
for(T item: collection){
action.call(item);
}
}
public interface Action<T> {
void call(T item);
}
說明: Action<T> 是泛型接口楔壤,其中call對item操作的方法鹤啡!
forEach方法2個參數(shù),第一個參數(shù)是需要處理的泛型集合蹲嚣, 第二個參數(shù)是操作處理递瑰。
并且,具體的對每個item做什么事情隙畜,在調(diào)用時候傳入抖部!
如何使用?
List<String> list = Arrays.asList("Hello", "World!");
forEach(list, new Action<String>() {
@Override
public void call(String item) {
out.println(item);
}
});
說明: 使用很簡單议惰,就是打印出List中的每個String慎颗, 當(dāng)然你也可以在call方法中做其他的操作。
lambda簡化
forEach(list, item -> out.println(item));
接下來言询,實(shí)現(xiàn)我們自己的filter函數(shù)俯萎, 這個是函數(shù)式編程中經(jīng)常使用的函數(shù)
filter
作用: 過濾Collection中符合條件的元素集合
簡化描述 T -> R
/** 需求:
*過濾出 [1, 2, ... 10]中是偶數(shù)的集合, 并打印出來!
*/
1. java 8 stream
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
integerList.stream()
.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer % 2 == 0; //判斷條件
}
})
.forEach(out::println);
lambda簡化版本
integerList.stream()
.filter(integer -> (integer % 2 == 0))
.forEach(out::println);
2. DIY filter
需要明確 filter 的作用是將 T -> T运杭, 可以理解為將集合 T 轉(zhuǎn)換為 集合 T夫啊, 其中后面的是前面的子集!
注意其中 2點(diǎn):
- 遍歷
- 有判斷操作
所以县习,filter函數(shù)設(shè)計為:
public static <T> Collection<T> filter(Collection<T> collection, Perdicate<T> perdicate ) {
Collection<T> result = new ArrayList<>();
for(T item : collection){
if(perdicate.call(item)) {
result.add(item);
}
}
return result;
}
public interface Perdicate<T> {
boolean call(T item); //判斷方法
}
說明: Perdicate<T> 是泛型接口涮母,其中perdicate.call(item)是對item的判斷,滿足條件加入到result 集合中
如何使用躁愿?
filter(integerList, new Perdicate<Integer>() {
@Override
public boolean call(Integer item) {
return item % 2 == 0;
}
}).forEach(out::println);
lambda簡化:
filter(integerList, item -> item % 2 == 0).forEach(out::println);
再如叛本, 找出集合["a1", "ab1", "a1", "ab2", "ac"]中全部 “a1”元素, 并打印
filter(Arrays.asList("a1", "ab1", "a1", "ab2", "ac"), item -> item.equals("a1")).forEach(out::println);
仔細(xì)看彤钟,邏輯清晰来候,寫法簡潔!
接下來逸雹,實(shí)現(xiàn)我們自己的find函數(shù)营搅, 這個是函數(shù)式編程中經(jīng)常使用的函數(shù)
find
作用: 找到集合中的第一個元素
注意, java 8 中提供了2個函數(shù)梆砸,findFirst转质, findAny, 為了簡單帖世,這里就是選擇返回集合第一個元素休蟹!
/** 需求:
* [1, 2, ... 10]中的第一個元素
*/
1. java 8 stream.findFirst
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
out.println(integerList.stream().findFirst().get());
2. DIY find
public static <T> T find(Collection<T> collection){
return collection.stream().findFirst().get();// 直接復(fù)用 stream 方法
}
小結(jié)
通過java 8 stream 和 DIY 函數(shù)對比,發(fā)現(xiàn)在Collection基礎(chǔ)上也是可以做到類似stream提供的函數(shù)。
大概揭示赂弓, 函數(shù)式常用方法的原理绑榴!
以上三個比較好理解,下篇的map盈魁,以及flatmap翔怎,不是很好理解!
代碼上傳到csdn 資源下載
喜歡杨耙,用實(shí)際點(diǎn)贊支持我吧赤套! 歡迎留言討論!