瞭解Stream
Java8中有兩大最為重要的改變。第一個是 Lambda 表達式;另外個則是 Stream API(java.util.stream.*)。
定義:Stream是數據渠道,用於操作數據源(集合、數組等)所生成的元素序列。“集合講的是數據,流講的是計算!”
注意:
①Stream 自己不會存儲元素。
②Stream 不改變源對象。相反,他們會返回一個持有結果的新Stream。
③Stream 操作是延遲執行的。這意味著他們會等到需要結果的時候才執行。(在使用Stream API時如果沒有終止操作,代碼不會執行)
Stream API 的操作步驟:
- 創建 Stream
- 中間操作
- 終止操作(終端操作)
創建 Stream,4種方法
- Collection 提供了兩個方法 stream() 與 parallelStream()
List<string> list = new ArrayList<>();
Stream<string> stream = list.stream(); //獲取一個順序流
Stream<string> parallelStream = list.parallelStream(); //獲取一個並行流
/<string>/<string>/<string>
- 通過 Arrays 中的 stream() 獲取一個數組流
Integer[] nums = new Integer[10];
Stream<integer> stream1 = Arrays.stream(nums);
/<integer>
- 通過 Stream 類中靜態方法 of()
Stream<integer> stream2 = Stream.of(1,2,3,4,5,6);
/<integer>
- 創建無限流
//迭代
//解析該方法:iterate(final T seed, final UnaryOperatorf)
//第一個參數:seed 為迭代的起始種子(起始數)
//第二個參數:UnaryOperator則是一個函數式接口,對傳入參數進行一元運算(x+2就是函數接口方法的具體實現)
//.limit(10)是中間操作
Stream<integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
stream3.forEach(System.out::println);
/<integer>
中間操作:多個中間操作可以連接起來形成一個流水線,除非流水線上觸發終止操作,否則中間操作不會執行任何的處理而在終止操作時一次性全部處理,稱為“惰性求值”。
篩選與切片
以limit()方法為例進行解釋
//如果有limit方法,因為Stream是延遲執行的,所以只有當e.getSalary() >= 5000;滿足時
//才會打印輸出語句,節省內存空間
public void test4(){
emps.stream()
.filter((e) -> {
System.out.println("短路!"); // && ||
return e.getSalary() >= 5000;
}).limit(3)
.forEach(System.out::println);
}
映射
比較map()方法和flatMap()方法的區別:前者是將獲得的元素的流以流的形式放入總的流中,後者則是將流中的元素單獨拿出來放入總的流中。(以下代碼,前者的返回值是嵌套的,後者則是總流)
//TestStreamAPI1是類名
//類中的靜態方法,傳入字符串返回拆分後字符數組的流
public static Stream<character> filterCharacter(String str){
List<character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
public void test1(){
List<string> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
Stream<stream>> stream2 = strList.stream()
.map(TestStreamAPI1::filterCharacter);
stream2.forEach((sm) -> {
sm.forEach(System.out::println);
});
System.out.println("---------------------------------------------");
Stream<character> stream3 = strList.stream()
.flatMap(TestStreamAPI1::filterCharacter);
stream3.forEach(System.out::println);
}
/<character>/<stream>/<string>/<character>/<character>
排序
Stream 的終止操作
查找和匹配
樣例代碼
//findAny()方法
Optional<employee> op2 = emps.parallelStream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.findAny();
System.out.println(op2.get());
//min()方法
Optional<employee> op2 = emps.stream()
.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op2.get());
/<employee>/<employee>
歸約
代碼
//reduce() 方法,第一個參數為起始值
//第二個參數:BinaryOperator是一個函數式接口,功能是傳入兩個參數,並對其進行操作,返回出來
//reduce()方法運行順序,將“0”作為起始值傳給“x”,數組中去第一個值傳給“y”,相加得值再賦值給“x”,數組中取第二個值賦值“y”,相加.....以此類推
public void test1(){
List<integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
}
/<integer>
收集
List<employee> emps = Arrays.asList(
new Employee(102, "李四", 79, 6666.66, Status.BUSY),
new Employee(101, "張三", 18, 9999.99, Status.FREE),
new Employee(104, "趙六", 8, 7777.77, Status.BUSY),
);
//求實例總數
Long count = emps.stream()
.collect(Collectors.counting());
/<employee>
Collector 接口中方法的實現決定了如何對流執行收集操作(如收集到 List、Set、Map)。但是 Collectors 實用類提供了很多靜態方法,可以方便地創建常見收集器實例,具體方法與實例如下表:
代碼實例
//多級分組
//先按狀態分組(status),然後再自定義按年齡分組
@Test
public void test6(){
Map<status>>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if(e.getAge() >= 60) {
return "老年";
} else if(e.getAge() >= 35) {
return "中年";
} else {
return "成年";
}
})));
System.out.println(map);
}
/<status>
並行流與串行流並行流
就是把一個內容分成多個數據塊,並用不同的線程分別處理每個數據塊的流。(底層使用Fork/Join 框架)
Java 8 中將並行進行了優化,我們可以很容易的對數據進行行操作。Stream API 可以聲明性地通過 parallel() 與sequential() 在並行流與順序流之間進行切換。
Long sum = LongStream.rangeClosed(0L, 10000000000L)
.parallel()
.sum();
容器類(Optional類)
Optional
閱讀更多 獨行者pxl 的文章