lambda表示式及Stream語法解析
本人也學習lambda沒有多長時間,有什麼不對的歡迎指正。由於馬上去實習會用到一些Java8新特性中lambda表示式的一些東西,抽出一晚上時間來對其進行一下簡單總結
目錄
1、通過幾個例子認識一下lambda表示式
public class test00 {
public static void main(String[] args) {
List<String> list = Arrays.asList("help","happle","java","ruby","net","cshop");
list.forEach(names->System.out .print(names.toUpperCase()+" "));
}
}
通過這個例子的很簡單明瞭lambda表示式如何實現了集合中所有字母大寫輸出。
public class Test01{
public static void main(String args[]){
//匿名內部類中的表示方法,前面的文章已經說明了什麼是匿名內部類,不清楚的可以翻閱一下
Runnable r = new Runnable(){
public void run(){
System.out.println("run " +Thread.currentThread().getName());
}
}
Thread t = new Thread(r);
t.start();
}
}
public class Test02{
public static void main(String args[]){
//lambda表示式表示方法
new Thread(()->{
System.out.println("begin");
}).start();
}
}
上面例子說明了lambda表示式表示匿名物件的簡潔。
其實,Java8的lambda表示式提供了表示單一SAM(Single Abstract Method)介面更加簡潔的方法。
2、lambda的好基友Stream
2.1、什麼是Stream?
stream是元素的集合,可以順序和並行的對元stream進行匯聚操作。stream就相當於高階版的Iterator。為什麼是高階版的Iterator,因為原版的只能一個一個遍歷元素並對其執行某些操作;但是stream,只要告訴它對其包含的元素執行什麼操作即可,其他的交給stream自己去處理。
List<String> list = Arrays.asList("abc","bcd","cef");
list.stream().filter(name- >name.startsWith("a")).forEach(name->System.out.println(name));
總結一下使用stream的基本步驟:
建立stream—>轉換Stream,每次轉換Stream物件不改變,返回一個新的Stream物件(可以進行多次轉換)—>對Stream進行聚合操作,獲取想要的結果。
2.2、建立Stream物件的方法有兩種途徑:
2.2.1、通過Stream介面的靜態方法(Java8裡介面可以帶靜態方法);
of方法:有兩個overload方法,一個接受變長引數,一個接受單一值,如下所示:
Stream<Integer> intList = Stream.of(1,2,3);
Stream<String> stringList = Stream.of("tianda");
generator方法:生成一個無限長度的Stream
Stream.generator(()->Math.random());
這個無限長度的Stream是懶載入,一般這種無限長度的Streamd都會配合Stream的limit()方法來用。
iterator方法:也能生成無限長度的Stream,和generator不同的是,它生成的元素重複對給定值呼叫使用者指定的函式來生成。即形式x,f(x),f(f(x))無限迴圈
Stream.iterator(1,a>a+1).limit(10).forEach(System.out::println);
2.2.2、通過Collection介面的預設方法(Java8的新特性,即介面中一個帶有實現的方法)—stream(),把一個Collection物件轉換成Stream物件。
public interface Collection<E> extends Iterable<E>{
default Stream<E> stream(){
return StreamSupport.stream(spliterator(), false);
}
}
2.2.3、轉換Stream
轉換即把一個Stream通過某些行為轉換成一個新的Stream。常用的方法有:
distinct:對Stream中包含的元素進行去重操作,生成的新的Stream中沒有重複元素。
filter:對Stream中包含的元素使用過濾函式進行過濾操作,生成符合條件的元素;
map:對於Stream中包含的元素使用給定的轉換函式進行轉換操作,新生成的Stream只包含轉換生成的元素。這個方法有三個對於原始型別的變換方法,分別是:mapToInt,mapToLong和maoToDouble。這三個方法好理解,主要是為了免除自動裝箱/拆箱的額外消耗。
flatMap:和map類似,不同的是每個元素轉換得到的是Stream物件,會把子Stream中的元素壓縮到父集合中;
peek:生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費函式,新的Stream每個元素被消費的時候都會執行給定的消費函式;
limit:對一個Stream進行截斷操作,獲取其前N個元素,如果原Stream中包含元素個數小於N,那就獲取其所有元素;
skip:返回一個丟棄原Stream的前N個元素後剩下的元素組成新的Stream,如果原Stream中包含的元素個數小於N,那麼返回空Stream。
介紹了這麼多小面我們寫一個例子
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,1,0,3,2,4,3);
list.stream().filter(num->num != 0).mapToInt(num->num+2).distinct().skip(1).limit(6).forEach(nam->System.out.print(nam+" "));
效能問題:對於一個Stream進行多次轉換操作,每次都會對元素進行轉換,而且是執行多次,這樣時間複雜度就是一個for迴圈裡把所有操作都做N次嗎。當然不是這樣,轉換操作都是lazy的,多個操作匯聚後才會進行,也就是就進行一次for迴圈即可。
2.2.4、匯聚:
匯聚操作簡單地說就是接受一個元素序列為輸入,反覆使用某個操作,把序列中的元素合併成一個彙總的結果。比方說找元素中的最大值。Stream介面有一些通用的匯聚操作,比如reduce()和collect();也有一些特定用途的匯聚操作,比如sum(),max()和count()。需要注意的是:sum()方法不是所有的Stream物件都有,只有IntStream、LongStream、DoubleStream的實力才有。
可變匯聚
把輸入的元素們累積到一個可變容器中,比如Collection或者StringBuilder;
可變匯聚對應的方法只有一個:collect,正如其名字顯示的,他可以把Stream中的要有元素手機到一個結果容器中(比如Collection)。同時,Java8提供了Collector的工具類-[Collectors]
詳細可參考
(http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html) 其中已經定義了一些靜態方法,比如:Coolectors.toCollection()收集到Collection中,Collectors.toList()收集到List中和Collectors.toSet()收集到Set中。其他的可以看文件。
List<Integer> nums = Arrays.toList(1,2,null,3,null,5,null,6);
List<Integer> num = nums.stream().filter(num->num != null).collect(Collectors.toList());
其他匯聚
除去可變匯聚剩下的,一般都不是通過反覆修改某個可變物件,而是通過把前一次的匯聚結果當成下一次的入參,反覆如此。比如reduce,count,allMatch。
reduce方法(最常用到的方法):reduce方法的工作原理,可以這樣概括:在對一個集合中的元素按照順序進行兩兩操作時,根據某種策略來得到一個結果,得到的結果將作為一個元素參與到下一次操作中,最終這個集合會被歸約成一個結果,這個結果就是reduce的一個返回值。
List<String> friends = Arrays.asList("adsds","adase","trggsf","yooy");
final Optional<String> aln = friends.stream().reduce((name1,name2)->name1.length()>=name2.length()?name1:name2);
aln.ifPresent(name->System.out.println(name));
我們要知道reduce方法返回的物件型別是一個Optional,這是因為待操作的集合可能為空。
集合中只有一個元素時,reduce會立即將該元素作為實際結果以Optional型別返回,這是不會呼叫lambda表示式的。
reduce方法是會按照集合的順序對其元素進行兩兩操作的,可以額外傳入一個值作為“基礎值”或者“預設值”。
final String ste = friends.stream().reduce("stream",(name1,name2)->name1.length()>name2.length()?name1:name2);
count方法:獲取Stream中元素的個數,比較簡單。
搜尋相關方法:
allMatch:是不是Stream中的所有元素都滿足給定的匹配條件 ;
anyMatch:Stream中是否存在任何一個元素滿足匹配條件;
findFirst: 返回Stream中的第一個元素,如果Stream為空,返回空Optional;
noneMatch:是不是Stream中的所有元素都不滿足給定的匹配條件;
max和min:使用給定的比較器(Operator),返回Stream中的最大值或最小值。
List<Integer> ints = Arrays.toList(1,2,3,4,5,6,7,8,9,10);
System.out.println(ints.stream().allMatch(item -> item < 100));
ints.stream().max((o1, o2) -> o1.compareTo(o2)).ifPresent(System.out::println);
繼續更新中!