java函數語言程式設計:流
java8對核心類庫的改進主要包括集合類的API和新引入的流(Stream)
流使程式設計師得以站在更高層次上對集合進行操作
介紹Stream類中的一組方法,每個方法都對應集合上的一種操作
外部迭代->內部迭代
計算倫敦藝術家人數:
int count = 0;
for(Artist artist : allArtists){
if(artist.isFrom("London")){
count++;
}
}
在集合上進行迭代,對每一個返回元素再進行處理
問題:
每次迭代集合時,都需要寫很多樣板程式碼,影響程式碼可讀性
for迴圈是一個封裝了迭代的語法糖,首先呼叫iterator方法產生一個新的Interator物件,
進而控制整個迭代過程,這就是外部迭代
迭代過程通過顯式呼叫Interator物件的hasNext和next方法完成迭代
int count = 0;
Interator<Artist> interator = allArtists.interator();
while(interator.hasNext()){
Artist artist = interator.next();
if(artist.isFrom("London")){
count++;
}
}
外部迭代的問題:
本質上是一種序列操作
使用for迴圈會將行為和方法混為一談
內部迭代:
另一種迭代方式就是內部迭代
long count = allArtists.stream() .filter(artist -> artist.isFrom("London")) .count();
stream()方法和interator()方法作用一樣,
該方法返回的並不是一個Interator物件,而是返回內部迭代中的相應介面Stream
Stream是用函數語言程式設計方式在集合類上進行復雜操作的工具
可被分解為兩步:
1,filter(artist -> artist.isFrom(“London”))找不所有來自倫敦的藝術家
2,count()計算他們的人數
每種操作都對應Stream介面的一個方法
找出來自倫敦的藝術家,需要對Stream物件進行過濾:filter
(過濾指:只保留通過某項測試的物件,測試由函式完成返回true或false)
count()方法計算給點Stream中包含的物件數量
實現機制
整個過程被分解為兩種簡單操作:過濾和計數
問題:兩種操作是否需要兩次迴圈?
事實上,返回的Stream物件不是一個新的集合,而是建立一個新集合的配方
惰性求值方法和早求值方法:
allArtists.stream()
.filter(artist -> artist.isFrom("London"))
filter只描述了stream,沒有產生新集合,這種方法稱為惰性求值方法
而count方法最終會從Stream產生值,這種方法稱為及早求值方法
驗證惰性求值方法
long count = allArtists.stream()
.filter(artist -> {
System.out.println(artist.getName());
return artist.isFrom("London")
});
在過路器中加入一句列印輸出藝術家名字,執行程式碼不會輸出任何資訊
驗證及早求值方法:
long count = allArtists.stream()
.filter(artist -> {
System.out.println(artist.getName());
return artist.isFrom("London")
})
.count();
在加入一個擁有終止操作的流,此時計數操作和藝術家名字都會被輸出
惰性求值方法和早求值方法的判斷
只需要看返回值即可,
若返回值是Stream就是惰性求值方法,
若返回值是一個值或空,就是及早求值方法
整個操作類似於建造者模式,使用一系列操作設定屬性和配置,最後呼叫build方法物件才被真正建立
常用流操作
1,collect(toList()):
由Stream裡的值生成一個列表,是一個及早求值操作
Stream的of方法使用一組初始值生成新的Stream
List<String> collected = Stream.of("a", "b", "c")
.collect(Collectors.toList());
首先由列表生成一個Stream,然後進行Stream的collect操作,由Stream生成列表
2,map
使用for迴圈將字串轉換為大寫
List<String> collected = new ArrayList<>();
for(String string : asList("a", "b", "hello")){
String uppercaseString = string.toUpperCase();
collected.add(uppercaseString);
}
使用map操作將字元創轉換為大寫
List<String> collected = Stream.of("a", "b", "hello")
.map(string -> string.toUpperCase())
.collect(collectors.toList)
Lambda表示式必須是Function介面的一個例項,
Function介面是隻包含一個引數的普通函式介面,引數和返回值不必屬於同一型別
3,filter
遍歷資料並檢查元素時使用
使用for迴圈遍歷列表,使用條件語句做判斷:
List<String> beginningWithNumbers = new ArrayList<>();
for(String value : asList("a", "1abc", "abc1")){
if(isDigit(value.charAt(0))){// 數字開頭
beginningWithNumbers.add(value);
}
}
使用函式式風格:
List<String> beginningWithNumbers = Stream.of("a", "1abc", "abc1")
.filter(value -> isDigit(value.charAt(0)))
.collect(toList())
for迴圈中的if是一個很強的訊號,可使用filter方法替代
和map相似,filter接受一個函式作為引數,該函式使用Lambda表示式表示,值為true的元素會被保留下來
該Lambda表示式的函式介面是Predicate介面(T->Predicate->boolean)
4,flatMap
將多個Stream連線成一個Stream
List<Integer> together = Stream.of(asList(1, 2), asList(3, 4))
.flatMap(numbers -> numbers.stream())
.collect(toList());
呼叫.stream()方法將每個列表轉換成Stream物件,其餘部分由flatMap方法處理
flatMap方法的函式介面也是Function,方法的返回值為Stream型別
5,max和min
在Stream上求最大值和最小值
使用for迴圈,根據曲目長度,查詢最短曲目
List<Track> tracks = asList(new Track("abc", 1000),
new Track("abc", 500),
new Track("abc", 700))
Track shortestTrack = tracks.get(0);
for(Track track : tracks){
if(track.getLength() < shortestTrack){
shortestTrack = track;
}
}
min()根據曲目長度,查詢最短曲目
List<Track> tracks = asList(new Track("abc", 1000),
new Track("abc", 500),
new Track("abc", 700))
Track shortestTrack = tracks.stream()
.min(Comparator.comparing(track -> track.getLength()))
.get();
使用Comparator物件中java8新提供的靜態方法comparing,實現一個比較器
comparing方法接受一個函式並返回另一個函式
還可以呼叫空Stream的max方法,返回Optional物件
Optional物件表示一個可能存在也可能不存在的值
如果Stream為空,該值不存在,如果不為空,則該值存在
通過呼叫get方法可以取出Optional物件中的值
6,reduce
reduce操作可以實現從一組值中生成一個值
count,min,max都是reduce方法
reduce的累加過程:
每一步都將Stream中的元素進行累加,遍歷至Stream中的最後一個元素時,得到累加值
使用reduce求和:
int count = Stream.of(1, 2, 3)
.reduce(0, (acc, element) -> acc + element);
傳入Stream的兩個引數分別是:當前元素和acc,
acc是累加器,儲存當前累加結果
Lambda表示式返回值為最新的acc,上一輪acc的值和當前元素的相加結果
reduce的型別是BinaryOperator
展開reduce操作:
BinaryOperator<Integer> accumulator = (acc, element) -> acc + element
int count = accumuator.apply(
accumuator.apply(
accumuator.apply(0, 1),
2),
3);
指令式程式設計求和:
int acc = 0;
for(Integer element : asList(1, 2, 3)){
acc = acc = element;
}
對於集合來說,迴圈在外部,且需要手動更新