show me the code and take to me,做的出來更要說的明白
GitHub項目JavaHouse同步收錄
喜歡就點個讚唄! 你的支持是我分享的動力!
引入
是否有遇到看不懂身邊同事代碼的情況,是否有被面試官問到 Java 新特性不懂的情況。我掐指一算你大概是遇到的了 Lambda 表達式 和 Stream 流。爲了解決上述情況,我特地獻上一份關於 Lambda 和 Stream 的指南,以解燃煤之急。
Lambda
普通調用
首先我們先看第一個例子。
@Test
public void test(){
test1(1);
}
private void test1(int a){
System.out.println(a);
}
運行Test,輸出結果
1
相信這裏大家都很清楚,我不需要多廢話!
內部類調用
那我們在來看看第二個例子
public interface IPerson {
void personMethod();
}
private void test2(IPerson person){
person.personMethod();
}
@Test
public void test2(){
test2(new IPerson(){
@Override
public void personMethod() {
System.out.println(1);
}
});
}
運行Test,輸出結果
1
這裏的話我將參變成了一個接口,使用了內部類。大家是不是覺得這種形式的代碼不容易看清楚,一個參數竟然放了這麼多東西,真是讓人頭大啊。所以我們有了 Lambda 表達式。
Lambda 重構
我們使用 Lambda 表達式重寫上面代碼。得到下面新代碼:
public interface IPerson {
void personMethod();
}
private void test2(IPerson person){
person.personMethod();
}
@Test
public void test2(){
test2(() -> System.out.println(1));
}
運行Test,輸出結果
1
這樣的代碼是不是比使用內部類代碼優雅了很多,看起來舒服極了,關鍵代碼也少了。這就是 Lambda 表達式的魅力。
Lambda 調用
好了,經過上面的層層遞進的例子,我們引出了 Lambda 表達式。現在我們開始瞭解 Lambda 表達式的語法。
語法:
->
是的,他的調用語法就是一個箭頭。當然這樣說的話我也不太信。其實還沒說完。
當我們的方法沒有參數的時候,沒有返回,他需要保留括號,形式如下:
() -> System.out.println(1);
當有一個參數的時候,沒有返回,小括號可以去掉,形式如下
a -> System.out.println(a)
當有多個參數的時候以及方法裏面的語法多餘一行的時候,沒有返回,,形式如下:
(a, b) -> {
System.out.println(a);
System.out.println(b);
}
上面例子都是沒有返回的,那麼有返回又是怎樣的呢?形式如下:
a -> {
System.out.println(a);
return a;
}
是的,那就加一下返回啊。
函數式接口
其實上面都是展示如果去調用,不知道你們是否發現後者感覺到,他沒有方法名就去調用了,是不是接受不了。這是正常情況。因爲我們調用的接口裏面這有一個方法,所以我們只需要一個 ->
就可以調用到接口裏面方法。
所以我們如果想自己寫一個 Lambda 的話。只需要在接口裏面寫一個抽象方法即可。舉了例子:
public interface IPerson {
int personMethod(int a);
}
當然我建議加一個註解@FunctionalInterface
,形式如下:
@FunctionalInterface
public interface IPerson {
int personMethod(int a);
}
這樣就是限制接口裏面只能有一個抽象方法了。這個就叫函數式接口。當我們再繼續往裏面加的話,就會報錯了:
其實大多數情況下,我們不需要自己寫函數式接口。因爲 Java 已經內置了四種常見的函數式接口。
這四個接口需要一般與 Stream 一起使用。
Stream
普通迭代
我們直接看代碼,一個普通的迭代例子
public class StreamTest {
@Test
public void test2(){
List list = Arrays.asList(1, 2, 3, 4, 5);
int count = 0;
for (Object o : list){
count ++;
}
System.out.println(count);
}
}
運行Test,輸出結果
5
在 Java8 之前,我們統計 list 的大小是上面的形式。
Stream 重構
現在我們用 Stream 重構上面的代碼
public class StreamTest {
@Test
public void test3(){
List list = Arrays.asList(1, 2, 3, 4, 5);
long count = list.stream().count();
System.out.println(count);
}
}
運行Test,輸出結果
5
瞬間感覺代碼清爽了很多,變得優雅了,關鍵代碼也少了。這就是 Stream 的魅力。
經過上面的例子,我們可以感覺到 Stream 可以代替 for 循環,進行特定操作。注意這裏是特定的操作,因爲 Stream 接口裏面只有只封了幾個方法。
collect()
collect() 方法可以將 Stream 變成 List等集合形式。
我們看一個例子:
public class StreamTest {
@Test
public void test4(){
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5,1);
List<Integer> collect = stream.collect(Collectors.toList());
}
}
fiter()
fiter() 方法與 Predicate 函數接口一起使用。
不想看文字,還有圖片
該方法可以過濾出特定元素並且返回原來的類型。舉一個例子
public class StreamTest {
@Test
public void test5(){
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5,1);
List<Integer> collect = stream.filter(a -> a > 3).collect(Collectors.toList());
System.out.println(Arrays.asList(collect));
}
}
運行Test,輸出結果
[[4, 5]]
map()
map() 方法與 Function 函數接口一起使用。
不想看文字,還有圖片
該方法可以將原來的集合進行修改,包括返回類型,然後返回一個新的集合。舉一個例子
public class StreamTest {
@Test
public void test8(){
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5,1);
List<Integer> collect = stream.map(a -> a = 1).collect(Collectors.toList());
// List<Boolean> collect = stream.map(a -> a == 1).collect(Collectors.toList());
System.out.println(Arrays.asList(collect));
}
}
運行Test,輸出結果
[[1, 1, 1, 1, 1, 1]]
我們可以看到兩種情況 Integer -> Integer 以及 Integer -> Boolean
注意我們不能操作同一個 stream 兩次,不然會報錯,有興趣可以試試。
distinct()
該方法意如其字,就是去重。代碼如下
public class StreamTest {
@Test
public void test7(){
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5,1);
List<Integer> collect = stream.distinct().collect(Collectors.toList());
System.out.println(Arrays.asList(collect));
}
}
運行Test,輸出結果
[[1, 2, 3, 4, 5]]