您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何使用java8新特性Stream”,在日常操作中,相信很多人在如何使用java8新特性Stream問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何使用java8新特性Stream”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
首先先定義一個菜品類:
Dish
public class Dish { private final String name; private final boolean vegetarian; private final int calories; private final Type type; public boolean isVegetarian() { return vegetarian; } //省略set,get,toString方法}
然后創建一個靜態方法,并且設置成類的成員變量,以供測試。
public static List<Dish> getDishes() { return Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT), new Dish("beef", false, 700, Dish.Type.MEAT), new Dish("chicken", false, 400, Dish.Type.MEAT), new Dish("french fries", true, 530, Dish.Type.OTHER), new Dish("rice", true, 350, Dish.Type.OTHER), new Dish("pizza", true, 550, Dish.Type.OTHER), new Dish("prawns", false, 300, Dish.Type.FISH), new Dish("salmon", false, 450, Dish.Type.FISH) ); }
XNBqZ
好了,現在有個需求,找出菜品中所有小于400卡路里的食物,并且按照卡路里的大小進行排序。
在java8之前,甚至有些人在java8之后,都會想著借助一個中間變量保符合要求的菜品,然后排序。
public static List<String> beforeJava8() { List<Dish> lowCaloricDishes = new ArrayList<>(); for (Dish dish : dishes) { if (dish.getCalories() < 400) { lowCaloricDishes.add(dish); } } lowCaloricDishes.sort(Comparator.comparingInt(Dish::getCalories));// lowCaloricDishes.sort((d1, d2) -> Integer.compare(d1.getCalories(), d2.getCalories())); List<String> res = new ArrayList<>(); for (Dish dish : lowCaloricDishes) { res.add(dish.getName()); } return res; }
由于前一篇文章講過了方法引用,所以這里就直接用,不過下面一行也有普通的Lambda表達式的書寫。
上述寫法有什么問題嗎,可以發現lowCaloricDishes
只使用了一次,真就一個臨時變量。那能不能跳過創建變量的過程,你直接把數據給我,我經過過濾排序后得到想要的呢,就和流水線一樣。
public static List<String> afterJava8() { return dishes.stream() .filter(dish -> dish.getCalories() < 400) .sorted(Comparator.comparing(Dish::getCalories)) .map(Dish::getName) .collect(Collectors.toList()); }
從支持數據處理操作的源生成的元素序列
流和集合有點類似,集合是數據結構,主要的目的是存儲和訪問元素,而流的主要目的是為了對元素進行一系列的操作。
通俗入門地來講,集合就相當于你一部電影下載,流就相當于在線觀看。其實只需要把流想成高級的集合即可。流有兩個重要的特點:
流水線: 很多流本身會返回一個流,這樣多個流就能鏈接起來和流水線一般。
內部迭代: 內部迭代也就是把迭代封裝起來,如collect(Collectors.toList)
,與之相對應的外部迭代則是for-each
。
值得注意的是,和迭代器類似,流只能遍歷一次 ,遍歷完就可以說這個流消費掉了。
流常用的構建方式有4種,其實要么是借助Stream
類的靜態方法,要么是借助別人的類的靜態方法。
由值創建流
由數組創建流
由文件生成流
由函數生成流
public static void buildStream() throws IOException { Stream<String> byValue = Stream.of("java8", "c++", "go"); Stream<Object> empty = Stream.empty(); int[] nums = {1, 2, 3, 4, 5}; IntStream byInts = Arrays.stream(nums); Stream<String> byFiles = Files.lines(Paths.get("")); Stream<Integer> byFunction1 = Stream.iterate(0, n -> n * 2); Stream<Double> byFunction2 = Stream.generate(Math::random); Stream<String> java = Stream.of("java"); }
可以連接起來的流操作稱為中間操作,關閉流的操作稱為終端操作
通俗地講,返回結果是流的操作稱為中間操作,放回的不是流的操作稱為終端操作。
image-20210414155605342
通過查找java8接口可以得知到哪些接口是中間操作,哪些接口時終端操作。由于那些接口描述得太過官方,估計我貼了也沒啥人會仔細看,所以想看的直接去官方查閱即可。
就按照官網上的java API順序來講述,小插一句,之前我一直沒有學流是主要是因為感覺接口會很多,怎么可能記得了這么多,其實這幾天看才發現真的很少,基本上不用記。
img
首先構建好一個數字列表:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 5, 5, 5, 6, 7);
中間操作有去重、過濾、截斷、查看、跳過、排序 ,這些相信大家都能夠明白是什么意思。
public static void midOperation() { numbers.stream() .distinct() .forEach(System.out::println); List<Integer> filter = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); numbers.stream() .limit(3) .forEach(System.out::println); numbers.stream() .peek(integer -> System.out.println("consume operation:" + integer)) .forEach(System.out::println); List<Integer> skip = numbers.stream() .skip(2) .collect(Collectors.toList()); numbers.stream() .sorted() .forEach(System.out::println); }
需要單獨拎出來說的是映射(map) 和扁平化映射(flatMap) ,注意,這里的map并不是hashmap的那個map,而是說把什么映射或者說轉化成了什么。
public static void midOperation() { List<String> map = numbers.stream() .map(Object::toString) //這里就是把int映射成了string .collect(Collectors.toList()); }
而對于扁平化映射,現在又有一個需求,現在有個單詞列表如{"hello", "world"},返回里面各不相同的字符,也就是要求返回List<String>
。
這還不簡單,把單詞映射成一個個字母,再去重就好了。
public static void flatMapDemoNoral() { List<String> words = Arrays.asList("hello", "world"); List<String[]> normal = words.stream() .map(str -> str.split("")) .distinct() .collect(Collectors.toList()); }
img
雖然確實也能達到效果,但是注意映射所用的函數是split()
,返回的是String[]
,因此整個返回的是List<String[]>
那我映射完后再把每個String[]
數組映射成流
public static void flatMapDemoMap() { List<String> words = Arrays.asList("hello", "world"); List<Stream<String>> usingMap = words.stream() .map(str -> str.split("")) .map(Arrays::stream) .distinct() .collect(Collectors.toList()); }
雖然摘掉了數組的帽子,但是返回的卻是List<Stream<String>>
。
flatMap
正是為了解決這種情況的
public static void flatMapDemoFlatMap() { List<String> words = Arrays.asList("hello", "world"); List<String> usingFlatMap = words.stream() .map(str -> str.split("")) .flatMap(Arrays::stream) .distinct() .collect(Collectors.toList()); }
可以簡單的理解,map是把每個元素映射成了獨立的流,而扁平化map是把元素保存了下來,最后映射成了一個流 。
終端操作除了上述寫例子的時候常用的collect()
和forEach()
還有查找和規約兩種大的方向。
因為沒啥好說的,直接上代碼就完了:
public static void endOperationFindAndMatch() { if (dishes.stream().noneMatch(Dish::isVegetarian)) { System.out.println("所有的菜品都是非素食"); } if (dishes.stream().allMatch(Dish::isVegetarian)) { System.out.println("所有的菜品都是素食"); } if (dishes.stream().anyMatch(Dish::isVegetarian)) { System.out.println("菜品中至少有一道菜是素食"); } Optional<Dish> any = dishes.stream() .filter(meal -> meal.getCalories() <= 1000) .findAny(); Optional<Dish> first = dishes.stream() .filter(meal -> meal.getCalories() <= 1000) .findFirst(); }
對流的規約操作的話,一般有普通操作也就是能直接調用接口的,還有一種就是借助reduce()
。
對于普通操作來說,像求和,最大值,最小值這些都是有接口對應的。
public static void endOperationCalculate() { long count = dishes.stream() .filter(meal -> meal.getCalories() <= 1000) .count(); Optional<Dish> max = dishes.stream() .max(Comparator.comparingInt(Dish::getCalories)); Optional<Dish> min = dishes.stream() .min(Comparator.comparing(Dish::getName)); }
但是如果說要求對元素求和,就要使用reduce()
image-20210417114209123
一般使用的是可以接受2個參數,一個是初始值,一個是BinaryOprator<T>
來將兩個元素結合起來產生的新值。
public static void reduceDemo() { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 5, 5, 5, 6, 7); Integer sum = numbers.stream().reduce(0, Integer::sum); //所有元素相乘也是比較簡單 Integer multi = numbers.stream().reduce(0, (a, b) -> a * b); //還有求最大值 Optional<Integer> max = numbers.stream().reduce(Integer::max); }
上面一直出現有返回值是Optional<T>
,它是一個容器類,代表一個值存在或者不存在,比如一開始的findAny()
,可能找不到符合條件的菜品。Java8引入的目的主要是/為了不要返回容易出現問題的null了。
就說幾個比較常用的api就好了至于其它的可以上網看下官方API,今天說的API已經夠多了
isPresent()
將在Optional
包含值的時候返回true,否則返回false
ifPresent(Consumer<T> block)
存在值的時候會執行給定的代碼塊
get()
存在值就返回值,否則拋出NoSuchElement
異常
orElse()
存在值就返回,否則就返回一個默認值
public static void optionalDemo() { //ifPresent dishes.stream() .filter(Dish::isVegetarian) .findAny() .ifPresent(dish -> System.out.println(dish.getName())); //isPresent boolean isLowCalories= dishes.stream() .filter(dish -> dish.getCalories() <= 1000) .findAny() .isPresent(); //get Optional<Dish> optional = dishes.stream() .filter(Dish::isVegetarian) .findAny(); if (optional.isPresent()) { Dish dish = optional.get(); } //orElse Dish dishNormal = dishes.stream() .filter(Dish::isVegetarian) .findAny() .orElse(new Dish("java", false, 10000, Dish.Type.OTHER)); }
到此,關于“如何使用java8新特性Stream”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。