首先給大家看一段代碼,讓大家直觀感受下 Java7 和 Java8 遍歷處理集合的不同
Dish 是一個菜肴物件,calories 屬性表示該菜品的卡路里值,name 則是菜品的名稱,我們需要過濾出卡路里小于400、然后根據卡路里值升序、接著拿到他們的名稱串列并回傳
Java7
public static List<String> getLowCaloricDishesNamesInJava7(List<Dish> dishes){
List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish d: dishes){
if(d.getCalories() < 400){
lowCaloricDishes.add(d);
}
}
List<String> lowCaloricDishesName = new ArrayList<>();
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
public int compare(Dish d1, Dish d2){
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
for(Dish d: lowCaloricDishes){
lowCaloricDishesName.add(d.getName());
}
return lowCaloricDishesName;
}
Java8
public static List<String> getLowCaloricDishesNamesInJava8(List<Dish> dishes){
return dishes.stream()
.filter(d -> d.getCalories() < 400)
.sorted(comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());
}
如果需要多核并行處理,則只需呼叫 dishes.parallelStream() 即可
在 Java8 之前,程式員需要通過 2次遍歷 + 一次集合排序才能完成的作業,Java8 只需要一個鏈式呼叫就可以解決,這就是 Stream 的強大之處

認識流
流是什么
流是 Java API 的新成員,允許程式員以宣告式的方式處理集合資料,并且支持鏈式呼叫、支持并行處理,用流處理的集合資料高效且易讀,Stream 的 10 種創建方式,這篇推薦你看下,更多Java8新特性教程關注公眾號Java技術堆疊回復Java獲取,
流與集合的異同
-
集合的主要功能是以一定的時間和空間復雜度存盤和訪問元素,而流主要是用于元素計算
-
集合中的元素可以隨意添加和洗掉,而流不能添加和洗掉元素
-
流的元素是按需計算的,只有當用到時他才會參與計算,而集合中的元素必須提前全都準備好
-
流只能遍歷一次,下面的代碼會報錯
java.lang.IllegalStateException: stream has already been operated upon or closed流已經被消費掉
List<String> names = Arrays.asList("Java8", "Lambdas", "In", "Action");
Stream<String> s = names.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);
- 集合采用外部迭代,流采用內部迭代,內部迭代意味著 Java 可以替你選擇更優的迭代策略和并行處理,而外部迭代如果程式員想著做個更有的迭代/采用并行就相當于“下次一定”??了
流操作分類
對流的操作可以分為兩類,可以繼續執行下一個流操作的稱為中間操作(方法的回傳值是 Stream),關閉流的操作稱為終止操作,
中間操作
除非流水線上執行終端操作,否則中間操作不會執行任何處理,流會對中間操作進行合并、短路等優化
終端操作
終端操作會從流的流水線生成結果,回傳一個非 stream 的任意型別值
使用流
篩選和切片
篩選
filter(Predicate<? super T> predicate) 方法可以將流中滿足某條件的元素篩選出來,該方法接收一個謂詞函式,回傳流,比如要選出某個蘋果集合中紅色的蘋果
List<Apple> appleList = new ArrayList<>();
List<Apple> redAppleList = appleList.stream().filter(a -> "red".equals(a.getColor())).collect(Collectors.toList());
去重
distinct() 方法會根據元素的 hashCode() 和 equals() 方法對流中元素進行去重操作
截斷
limit(n) 方法會回傳流的前 n 個元素,對于有序集合List,流會按照添加順序回傳前 n 個元素,而無序集合則不會
跳過
skip(n) 方法會跳過流的前 n 個元素,可以通過 skip(m).limit(n) 回傳串列中第 m - (m+n) 區間的元素,類似與 mysql 中的 limit m,n
映射
對流中的每個元素應用函式
map(Function<? super T, ? extends R> mapper) 方法,該方法接收一個 Function 函式,對流中的每一個元素使用,然后可以回傳任意型別的物件,有了該方法,就可以結合 Lambda 運算式對集合中的元素使用函式進行各種轉換
流的扁平化
flatMap() 可以將流操作中多個流合并成一個流的多個元素,
舉個例子:集合 words 有兩個單詞,現在想獲得[H, e, l, o, W, r, d] 在 split 方法執行完畢后,回傳的是 Stream(String[]) 物件,而此時如果執行 map 方法,回傳的就是多個流的集合(這個例子中就是兩個 Stream(String)),這時是無法繼續接下來的 distinct 操作的,因此需要 flatMap 將兩個 Stream扁平化成一個 Stream,然后進行操作
List<String> words = Arrays.asList("Hello", "World");
List<String> charList = words.stream().map(word -> word.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
該方法的方法宣告 flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) 中可以看出,他所使用的函式式介面 Function 第二個泛型 R 必須是 Stream流,即函式式介面的抽象方法回傳值必須是 Stream流及其子類物件,
查找和匹配
檢查謂詞是否至少匹配一個元素
anyMatch 方法可以回答“流中是否存在至少一個復合謂詞條件的元素”回傳 boolean 型別的值,因此是一個終端操作,例如
List<Integer> num = Arrays.asList(1, 2, 3, 4, 5, 6);
if (num.stream().anyMatch(n -> n % 3 == 0)) {
System.out.println("集合中有元素是3的整數倍");
}
控制臺會輸出'集合中有元素是3的整數倍',因為集合中 3、6都是3的整數倍,符合謂詞的條件,
檢查謂詞是否匹配所有元素
allMatch 方法和 anyMatch 方法原理類似,但是它僅當所有元素滿足謂詞條件時,回傳 true,
noneMatch 與 allMatch 正好相反,僅當所有元素不滿足謂詞條件時,回傳 true
ps:和 && || 運算子類似,以上三個操作都用到了短路的思想來提高效率,
查找元素
findAny() 該方法回傳當前流中的任意元素,可以和其他流操作結合使用,這里需要注意 findAny() 回傳的結果被 Optional 所包裹,Optional是 Java8 為優雅的避免 NPE 所采用的新 API,這里需要說明的就是 Optional.ifPresent(Consumer<? super T> consumer) 表示當 Optional 包裹的元素不為空時,執行 consumer
num.stream().filter(n -> n > 2).findAny().ifPresent(System.out::println);
findFirst() 該方法回傳當前流中的第一個元素,一般也和其他流操作(例如 filter() 過濾)結合使用,與 findAny() 不同的是,他一定回傳有序集合的第一個滿足條件的元素,當然有得必有失,作為代價,findFirst() 在并行處理時限制更多一些,
歸約
元素求和
reduce(T identity, BinaryOperator<T> accumulator); 方法接收兩個引數:identity 初始值,accumulator 對兩個數的操作,例如求集合中數字的和:
num.stream().reduce(0, (a, b) -> a + b) // 計算完成,回傳 21
ps:Lambda 運算式 (a, b) -> a + b) 中 a 是上一輪執行完后的累計值,b 是本次回圈流中的元素,通過累加就可以計算出數字的和,更多Java8新特性教程關注公眾號Java技術堆疊回復Java獲取,
最大值和最小值
reduce 方法不僅可以求和、求積,甚至可以計算最大值、最小值,
num.stream().reduce(Integer::max);
num.stream().reduce(Integer::min);
總結
-
流是 Java API 的新成員,允許程式員以宣告式的方式處理集合資料,并且支持鏈式呼叫、支持并行處理,用流處理的集合資料高效且易讀,
-
流的API中可以分為兩大類,中間操作和終端操作,中間操作回傳流物件,可以鏈式呼叫,終端操作則回傳非流物件,
-
流提供了很多方便的API,如篩選 filter、去重 distinct、截斷 limit、跳過 skip、函式轉換 map、扁平化 flatMap、判斷流中是否有任意元素符合要求 anyMatch、是否所有元素都符合要求 allMatch、是否所有元素都不符合要求 noneMatch、查找元素 findAny findFirst、累計式的計算元素 reduce
碼字不易,如果你覺得讀完以后有識訓,不妨點個推薦讓更多的人看到吧!
著作權申明:本文首發于博客園,作者:后青春期的Keats
地址:https://www.cnblogs.com/keatsCoder/
關注公眾號Java技術堆疊回復"面試"獲取我整理的2020最全面試題及答案,
推薦去我的博客閱讀更多:
1.Java JVM、集合、多執行緒、新特性系列教程
2.Spring MVC、Spring Boot、Spring Cloud 系列教程
3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程
4.Java、后端、架構、阿里巴巴等大廠最新面試題
覺得不錯,別忘了點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/141638.html
標籤:Java
下一篇:Java8——Stream流
