寫在前面
如果你出去面試,面試官問了你關於Java8 Stream API的一些問題,比如:Java8中建立Stream流有哪幾種方式?(可以參見:《【Java8新特性】面試官問我:Java8中建立Stream流有哪幾種方式?》)Java8中的Stream API有哪些中間操作?(可以參見:《【Java8新特性】Stream API有哪些中間操作?看完你也可以吊打面試官!!》)如果你都很好的回答了這些問題,那麼,面試官可能又會問你:Java8中的Stream API有哪些終止操作呢?沒錯,這就是Java8中有關Stream API的靈魂三問!不要覺得是面試官在為難你,只有你掌握了這些細節,你就可以反過來吊打面試官了!
Stream的終止操作
終端操作會從流的流水線生成結果。其結果可以是任何不是流的值,例如: List、 Integer、Double、String等等,甚至是 void 。
在Java8中,Stream的終止操作可以分為:查詢與匹配、規約和收集。接下來,我們就分別簡單說明下這些終止操作。
查詢與匹配
Stream API中有關查詢與匹配的方法如下表所示。
方法 | 描述 |
---|---|
allMatch(Predicate p) | 檢查是否匹配所有元素 |
anyMatch(Predicate p) | 檢查是否至少匹配一個元素 |
noneMatch(Predicate p) | 檢查是否沒有匹配所有元素 |
findFirst() | 返回第一個元素 |
findAny() | 返回當前流中的任意元素 |
count() | 返回流中元素總數 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 內部迭代(使用 Collection 介面需要使用者去做迭代,稱為外部迭代。相反, Stream API 使用內部迭代) |
同樣的,我們對每個重要的方法進行簡單的示例說明,這裡,我們首先建立一個Employee類,Employee類的定義如下所示。
@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {
private static final long serialVersionUID = -9079722457749166858L;
private String name;
private Integer age;
private Double salary;
private Stauts stauts;
public enum Stauts{
WORKING,
SLEEPING,
VOCATION
}
}
接下來,我們在測試類中定義一個用於測試的集合employees,如下所示。
protected List<Employee> employees = Arrays.asList(
new Employee("張三", 18, 9999.99, Employee.Stauts.SLEEPING),
new Employee("李四", 38, 5555.55, Employee.Stauts.WORKING),
new Employee("王五", 60, 6666.66, Employee.Stauts.WORKING),
new Employee("趙六", 8, 7777.77, Employee.Stauts.SLEEPING),
new Employee("田七", 58, 3333.33, Employee.Stauts.VOCATION)
);
好了,準備工作就緒了。接下來,我們就開始測試Stream的每個終止方法。
1.allMatch()
allMatch()方法表示檢查是否匹配所有元素。其在Stream介面中的定義如下所示。
boolean allMatch(Predicate<? super T> predicate);
我們可以通過類似如下示例來使用allMatch()方法。
boolean match = employees.stream().allMatch((e) -> Employee.Stauts.SLEEPING.equals(e.getStauts()));
System.out.println(match);
注意:使用allMatch()方法時,只有所有的元素都匹配條件時,allMatch()方法才會返回true。
2.anyMatch()方法
anyMatch方法表示檢查是否至少匹配一個元素。其在Stream介面中的定義如下所示。
boolean anyMatch(Predicate<? super T> predicate);
我們可以通過類似如下示例來使用anyMatch()方法。
boolean match = employees.stream().anyMatch((e) -> Employee.Stauts.SLEEPING.equals(e.getStauts()));
System.out.println(match);
注意:使用anyMatch()方法時,只要有任意一個元素符合條件,anyMatch()方法就會返回true。
3.noneMatch()方法
noneMatch()方法表示檢查是否沒有匹配所有元素。其在Stream介面中的定義如下所示。
boolean noneMatch(Predicate<? super T> predicate);
我們可以通過類似如下示例來使用noneMatch()方法。
boolean match = employees.stream().noneMatch((e) -> Employee.Stauts.SLEEPING.equals(e.getStauts()));
System.out.println(match);
注意:使用noneMatch()方法時,只有所有的元素都不符合條件時,noneMatch()方法才會返回true。
4.findFirst()方法
findFirst()方法表示返回第一個元素。其在Stream介面中的定義如下所示。
Optional<T> findFirst();
我們可以通過類似如下示例來使用findFirst()方法。
Optional<Employee> op = employees.stream().sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst();
System.out.println(op.get());
5.findAny()方法
findAny()方法表示返回當前流中的任意元素。其在Stream介面中的定義如下所示。
Optional<T> findAny();
我們可以通過類似如下示例來使用findAny()方法。
Optional<Employee> op = employees.stream().filter((e) -> Employee.Stauts.WORKING.equals(e.getStauts())).findFirst();
System.out.println(op.get());
6.count()方法
count()方法表示返回流中元素總數。其在Stream介面中的定義如下所示。
long count();
我們可以通過類似如下示例來使用count()方法。
long count = employees.stream().count();
System.out.println(count);
7.max()方法
max()方法表示返回流中最大值。其在Stream介面中的定義如下所示。
Optional<T> max(Comparator<? super T> comparator);
我們可以通過類似如下示例來使用max()方法。
Optional<Employee> op = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op.get());
8.min()方法
min()方法表示返回流中最小值。其在Stream介面中的定義如下所示。
Optional<T> min(Comparator<? super T> comparator);
我們可以通過類似如下示例來使用min()方法。
Optional<Double> op = employees.stream().map(Employee::getSalary).min(Double::compare);
System.out.println(op.get());
9.forEach()方法
forEach()方法表示內部迭代(使用 Collection 介面需要使用者去做迭代,稱為外部迭代。相反, Stream API 使用內部迭代)。其在Stream介面內部的定義如下所示。
void forEach(Consumer<? super T> action);
我們可以通過類似如下示例來使用forEach()方法。
employees.stream().forEach(System.out::println);
規約
Stream API中有關規約的方法如下表所示。
方法 | 描述 |
---|---|
reduce(T iden, BinaryOperator b) | 可以將流中元素反覆結合起來,得到一個值。 返回 T |
reduce(BinaryOperator b) | 可以將流中元素反覆結合起來,得到一個值。 返回 Optional |
reduce()方法在Stream介面中的定義如下所示。
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
我們可以通過類似如下示例來使用reduce方法。
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);
System.out.println(sum);
System.out.println("----------------------------------------");
Optional<Double> op = employees.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(op.get());
我們也可以搜尋employees列表中“張”出現的次數。
Optional<Integer> sum = employees.stream()
.map(Employee::getName)
.flatMap(TestStreamAPI1::filterCharacter)
.map((ch) -> {
if(ch.equals('六'))
return 1;
else
return 0;
}).reduce(Integer::sum);
System.out.println(sum.get());
注意:上述例子使用了硬編碼的方式來累加某個具體值,大家在實際工作中再優化程式碼。
收集
方法 | 描述 |
---|---|
collect(Collector c) | 將流轉換為其他形式。接收一個 Collector介面的實現,用於給Stream中元素做彙總的方法 |
collect()方法在Stream介面中的定義如下所示。
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
我們可以通過類似如下示例來使用collect方法。
Optional<Double> max = employees.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
Optional<Employee> op = employees.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
Long count = employees.stream().collect(Collectors.counting());
System.out.println(count);
System.out.println("--------------------------------------------");
DoubleSummaryStatistics dss = employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
如何收集Stream流?
Collector介面中方法的實現決定了如何對流執行收集操作(如收集到 List、 Set、 Map)。 Collectors實用類提供了很多靜態方法,可以方便地建立常見收集器例項, 具體方法與例項如下表:
方法 | 返回型別 | 作用 |
---|---|---|
toList | List |
把流中元素收集到List |
toSet | Set |
把流中元素收集到Set |
toCollection | Collection |
把流中元素收集到建立的集合 |
counting | Long | 計算流中元素的個數 |
summingInt | Integer | 對流中元素的整數屬性求和 |
averagingInt | Double | 計算流中元素Integer屬性的平均 值 |
summarizingInt | IntSummaryStatistics | 收集流中Integer屬性的統計值。 如:平均值 |
joining | String | 連線流中每個字串 |
maxBy | Optional |
根據比較器選擇最大值 |
minBy | Optional |
根據比較器選擇最小值 |
reducing | 歸約產生的型別 | 從一個作為累加器的初始值 開始,利用BinaryOperator與 流中元素逐個結合,從而歸 約成單個值 |
collectingAndThen | 轉換函式返回的型別 | 包裹另一個收集器,對其結 果轉換函式 |
groupingBy | Map<K, List |
根據某屬性值對流分組,屬 性為K,結果為V |
partitioningBy | Map<Boolean, List |
根據true或false進行分割槽 |
每個方法對應的使用示例如下表所示。
方法 | 使用示例 |
---|---|
toList | List |
toSet | Set |
toCollection | Collection |
counting | long count = list.stream().collect(Collectors.counting()); |
summingInt | int total=list.stream().collect(Collectors.summingInt(Employee::getSalary)); |
averagingInt | double avg= list.stream().collect(Collectors.averagingInt(Employee::getSalary)) |
summarizingInt | IntSummaryStatistics iss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); |
Collectors | String str= list.stream().map(Employee::getName).collect(Collectors.joining()); |
maxBy | Optional |
minBy | Optional |
reducing | int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum)); |
collectingAndThen | int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); |
groupingBy | Map<Emp.Status, List |
partitioningBy | Map<Boolean,List |
public void test4(){
Optional<Double> max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
Optional<Employee> op = emps.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
Double avg = emps.stream()
.collect(Collecors.averagingDouble(Employee::getSalary));
System.out.println(avg);
Long count = emps.stream()
.collect(Collectors.counting());
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
寫在最後
如果覺得文章對你有點幫助,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習Java8新特性。
最後,附上Java8新特性核心知識圖,祝大家在學習Java8新特性時少走彎路。