Java 8 API添加了一個(gè)新的抽象稱為流Stream,可以讓你以一種聲明的方式處理數(shù)據(jù)。
Stream 使用一種類似用 SQL 語句從數(shù)據(jù)庫查詢數(shù)據(jù)的直觀方式來提供一種對 Java 集合運(yùn)算和表達(dá)的高階抽象。
Stream API可以極大提高Java程序員的生產(chǎn)力,讓程序員寫出高效率、干凈、簡潔的代碼。
這種風(fēng)格將要處理的元素集合看作一種流, 流在管道中傳輸, 并且可以在管道的節(jié)點(diǎn)上進(jìn)行處理, 比如篩選, 排序,聚合等。
元素流在管道中經(jīng)過中間操作(intermediate operation)的處理,最后由最終操作(terminal operation)得到前面處理的結(jié)果。
什么是 Stream?
Stream(流)是一個(gè)來自數(shù)據(jù)源的元素隊(duì)列并支持聚合操作
- 元素是特定類型的對象,形成一個(gè)隊(duì)列。 Java中的Stream并不會存儲元素,而是按需計(jì)算。
- 數(shù)據(jù)源 流的來源。 可以是集合,數(shù)組,I/O channel, 產(chǎn)生器generator 等。
- 聚合操作 類似SQL語句一樣的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作還有兩個(gè)基礎(chǔ)的特征:
- Pipelining: 中間操作都會返回流對象本身。 這樣多個(gè)操作可以串聯(lián)成一個(gè)管道, 如同流式風(fēng)格(fluent style)。 這樣做可以對操作進(jìn)行優(yōu)化, 比如延遲執(zhí)行(laziness)和短路( short-circuiting)。
- 內(nèi)部迭代: 以前對集合遍歷都是通過Iterator或者For-Each的方式, 顯式的在集合外部進(jìn)行迭代, 這叫做外部迭代。 Stream提供了內(nèi)部迭代的方式, 通過訪問者模式(Visitor)實(shí)現(xiàn)。
一、流的創(chuàng)建
- stream() ? 為集合創(chuàng)建串行流。
- parallelStream() ? 為集合創(chuàng)建并行流。parallelStream其實(shí)就是一個(gè)并行執(zhí)行的流.它通過默認(rèn)的ForkJoinPool,可能提高你的多線程任務(wù)的速度。并行流在遍歷時(shí)可能是無序的。
public class ParallelStream {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.stream().forEach(System.out::print);
}
}
二、 forEach、map、filter、limit、sorted
numbers.stream().forEach(System.out::print);
numbers.stream().forEach(i->System.out.print(i));
上述兩種方法是等價(jià)的。
forEach
forEach用來對stream中的數(shù)據(jù)進(jìn)行迭代,比如上面創(chuàng)建流的操作就使用了forEach。看會上面的例子后理解forEach不會很難的。需要注意的是,forEach操作是不能改變遍歷對象本身的。
Map
map 方法用于映射每個(gè)元素到對應(yīng)的結(jié)果,多數(shù)情況下用來處理數(shù)據(jù)。下面給出一個(gè)讓原list個(gè)位置元素自增2的代碼:
public class MapDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> outPutList = numbers.stream().map(i -> i + 2).distinct().collect(Collectors.toList());
outPutList.forEach(n->System.out.print(n+" "));
}
}
filter
filter 方法用于通過設(shè)置的條件過濾出元素。以下代碼片段使用 filter 方法過濾出空字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 獲取空字符串的數(shù)量
int count = strings.stream().filter(string -> string.isEmpty()).count();
Limit
limit 方法用于獲取指定數(shù)量的流。 以下代碼片段使用 limit 方法打印出 10 條數(shù)據(jù):
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
還有一個(gè)常用的是配合skip()方法用來進(jìn)行分頁操作。
int pageSize=10;
int currentPage=1;
return pageList.stream()
.skip(pageSize * (currentPage-1))
.limit(pageSize)
.collect(Collectors.toList());
sorted
sorted 方法用于對流進(jìn)行排序。以下代碼片段使用 sorted 方法對輸出的 10 個(gè)隨機(jī)數(shù)進(jìn)行排序:
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
Collectors
Collectors 可用于返回列表或字符串,上面介紹map的例子就用到了Collectors,下面給出菜鳥教程的一個(gè)例子:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("篩選列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
三、 統(tǒng)計(jì)
顧名思義,統(tǒng)計(jì)就是用來統(tǒng)計(jì)數(shù)據(jù)的,一般用于int、double、long等基本類型上。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的數(shù) : " + stats.getMax());
System.out.println("列表中最小的數(shù) : " + stats.getMin());
System.out.println("所有數(shù)之和 : " + stats.getSum());
System.out.println("平均數(shù) : " + stats.getAverage());
Stream先介紹到這里,我們Stream代碼覺得陌生是因?yàn)閯偨佑|聲明式編程的風(fēng)格,下一篇應(yīng)該會介紹lambda表達(dá)式和Optional的用法,我們會更多地用聲明式的編程風(fēng)格。