定義
A sequence of elements supporting sequential and parallel aggregate operations.
這個定義包含下面兩層意思
(1)Stream是元素的集合咒林;
(2)可以支持順序和并行的對原Stream進行匯聚的操作;
初體驗
以集合為例倾鲫,看Steam API如何極大的簡化了集合操作(當然召衔,Streams不止可以作用在集合上)
/**
* Created by haicheng.lhc on 17/04/2017.
*
* @author haicheng.lhc
* @date 2017/04/17
*/
public class Streams {
private enum Status {
OPEN, CLOSED
}
;
private static final class Task {
private final Status status;
private final Integer points;
Task(final Status status, final Integer points) {
this.status = status;
this.points = points;
}
public Integer getPoints() {
return points;
}
public Status getStatus() {
return status;
}
@Override
public String toString() {
return String.format("[%s, %d]", status, points);
}
}
}
每個task有一個狀態(tài):OPEN或是CLOSED慨灭,及該狀態(tài)對應的數(shù)目纳猫。如果我們有如下一個數(shù)組户盯,現(xiàn)在需要統(tǒng)計里面OPEN狀態(tài)的數(shù)目屯伞。
final Collection< Task > tasks = Arrays.asList(
new Task( Status.OPEN, 5 ),
new Task( Status.OPEN, 13 ),
new Task( Status.CLOSED, 8 )
);
按照傳統(tǒng)做法需要遍歷數(shù)組,然后計算出OPEN狀態(tài)的數(shù)目和挣磨。
但是在JAVA8里面雇逞,使用Stream就會變得很簡單
// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
.stream()
.filter( task -> task.getStatus() == Status.OPEN )
.mapToInt( Task::getPoints )
.sum();
System.out.println( "Total points: " + totalPointsOfOpenTasks );
Stream使用說明
- 官方文檔
- Stream操作分為
中間操作(intermediate operations )
和晚期操作(terminal operations)
-
中間操作(intermediate operations )
(例如Filter)會返回一個新的stream,它會根據(jù)條件從源stream里面過濾出符合的項放到新的stream里茁裙,不會影響源stream喝峦。這個過濾動作是延遲發(fā)生的,直到晚期操作(terminal operations)
才會真正執(zhí)行呜达。
Intermediate operations return a new stream. They are always lazy; executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.
-
中間操作(intermediate operations )
分為有狀態(tài)操作(stateful operations )
和無狀態(tài)兩種(stateless operations)
Intermediate operations are further divided into stateless and stateful operations. Stateless operations, such as filter and map, retain no state from previously seen element when processing a new element -- each element can be processed independently of operations on other elements. Stateful operations, such as distinct and sorted, may incorporate state from previously seen elements when processing new elements.
-晚期操作(terminal operations)
(如forEach 或sum)會遍歷stream并得出結(jié)果。在執(zhí)行晚期操作后粟耻,stream已經(jīng)被處理完了查近,不能再次被使用了。
Terminal operations, such as Stream.forEach or IntStream.sum, may traverse the stream to produce a result or a side-effect. After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used; if you need to traverse the same data source again, you must return to the data source to get a new stream. In almost all cases, terminal operations are eager, completing their traversal of the data source and processing of the pipeline before returning. Only the terminal operations iterator() and spliterator() are not; these are provided as an "escape hatch" to enable arbitrary client-controlled pipeline traversals in the event that the existing operations are not sufficient to the task.
- 延遲處理stream是出于性能考慮
Processing streams lazily allows for significant efficiencies; in a pipeline such as the filter-map-sum example above, filtering, mapping, and summing can be fused into a single pass on the data, with minimal intermediate state. Laziness also allows avoiding examining all the data when it is not necessary; for operations such as "find the first string longer than 1000 characters", it is only necessary to examine just enough strings to find one that has the desired characteristics without examining all of the strings available from the source. (This behavior becomes even more important when the input stream is infinite and not merely large.)
Stream語法
List<Integer> nums = Lists.newArrayList(1,null,3,4,null,6);
nums.stream().filter(num -> num != null).count();
這段代碼是獲取一個List中挤忙,元素不為null的個數(shù),可以看出語法為
紅色框中的語句是一個Stream的生命開始的地方霜威,負責創(chuàng)建一個Stream實例;
綠色框中的語句是賦予Stream靈魂的地方册烈,把一個Stream轉(zhuǎn)換成另外一個Stream戈泼,紅框的語句生成的是一個包含所有nums變量的Stream,進過綠框的filter方法以后赏僧,重新生成了一個過濾掉原nums列表所有null以后的Stream大猛;
藍色框中的語句是豐收的地方,把Stream的里面包含的內(nèi)容按照某種算法來匯聚成一個值淀零,例子中是獲取Stream中包含的元素個數(shù)
如何創(chuàng)建Stream
1挽绩、通過 Collection的stream() 或是parallelStream() 方法
public interface Collection<E> extends Iterable<E> {
//其他方法省略
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
}
2、通過Arrays.stream(Object[]) 方法
3驾中、使用Stream靜態(tài)方法來創(chuàng)建Stream
Stream<Integer> integerStream = Stream.of(1, 2, 3, 5);
Stream<String> stringStream = Stream.of("taobao");
轉(zhuǎn)換Stream[中間操作(intermediate operations )]
轉(zhuǎn)換Stream就是通過某種方法將原來的Stream轉(zhuǎn)換成另外一種Stream唉堪,但是源Stream不變模聋。常用的方法有:
1、map:
對于Stream中包含的元素使用給定的轉(zhuǎn)換函數(shù)進行轉(zhuǎn)換操作唠亚,新生成的Stream只包含轉(zhuǎn)換生成的元素链方。這個方法有三個對于原始類型的變種方法,分別是:mapToInt灶搜,mapToLong和mapToDouble祟蚀。這三個方法也比較好理解,比如mapToInt就是把原始Stream轉(zhuǎn)換成一個新的Stream占调,這個新生成的Stream中的元素都是int類型暂题。之所以會有這樣三個變種方法,可以免除自動裝箱/拆箱的額外消耗
2究珊、filter:
對于Stream中包含的元素使用給定的過濾函數(shù)進行過濾操作薪者,新生成的Stream只包含符合條件的元素
3、 distinct:
對于Stream中包含的元素進行去重操作(去重邏輯依賴元素的equals方法)剿涮,新生成的Stream中沒有重復的元素
4言津、 limit:
對一個Stream進行截斷操作,獲取其前N個元素取试,如果原Stream中包含的元素個數(shù)小于N悬槽,那就獲取其所有的元素;
5瞬浓、skip:
返回一個丟棄原Stream的前N個元素后剩下元素組成的新Stream初婆,如果原Stream中包含的元素個數(shù)小于N,那么返回空Stream猿棉;
6磅叛、peek:
生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費函數(shù)(Consumer實例)萨赁,新Stream每個元素被消費的時候都會執(zhí)行給定的消費函數(shù)弊琴;
This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline:
IntStream.of(1, 2, 3, 4)
.filter(e -> e > 2)
.peek(e -> System.out.println("Filtered value: " + e))
.map(e -> e * e)
.peek(e -> System.out.println("Mapped value: " + e))
.sum();
這個例子的輸出為
7、對一個stream執(zhí)行上面操作的組合
List<Integer> nums = Arrays.asList(1, 1, null, 2, 3, 4, null, 5, 6, 7, 8, 9, 10);
System.out.println("sum is:" + nums.stream()
.filter(num -> num != null)
.distinct()
.mapToInt(num -> num * 2)
.peek(System.out::println)
.skip(2)
.limit(4)
.sum());
輸出結(jié)果為
匯聚(Reduce)Stream[晚期操作(terminal operations)]
官方定義
A reduction operation (also called a fold) takes a sequence of input elements and combines them into a single summary result by repeated application of a combining operation, such as finding the sum or maximum of a set of numbers, or accumulating elements into a list. The streams classes have multiple forms of general reduction operations, called reduce()
and collect()
, as well as multiple specialized reduction forms such as sum()
, max()
, or count()
.
collect
- 定義
<R, A> R collect(Collector<? super T, A, R> collector);
- 使用例子
List<Integer> nums = Arrays.asList(1, 1, null, 2, 3, 4, null, 5, 6, 7, 8, 9, 10);
List<Integer> numsWithoutNull = nums.stream().filter(num -> num != null).
collect(Collectors.toList());
reduce
max和min:
使用給定的比較器(Operator)杖爽,返回Stream中的最大|最小值