1. 程式人生 > >我們一起來學java8的lambda表示式Stream

我們一起來學java8的lambda表示式Stream

Java 8 函數語言程式設計風格

Java 迄今為止最令人激動的特徵。這些新的語言特徵允許採用函式式風格來進行編碼,我們可以用這些特性完成許多有趣的功能。這些特性如此有趣以至於被認為是不合理的.他們說會影響計算速度,但是雖然是真的,但是存在皆合理.

所以我們摒棄缺點,研究優點.

演練

 

/**
 * @Title: Dog.java
 * @Description: Stream的演練
 * @author LiJing
 * @date 2018/12/3 14:13
 * @version 1.0.0
 */

import com.google.common.collect.Lists;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.text.Collator; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @Data @NoArgsConstructor @AllArgsConstructor public class Dog {
private Long id;//編號 private String name;//名字 private Integer age;//年齡 private String team;//分隊 public static void main(String[] args) { List<Dog> list = Lists.newArrayList(); list.add(new Dog(5L, "甜甜", 2, "M")); list.add(new Dog(1L, "豆豆", 5, "F")); list.add(
new Dog(2L, "苗苗", 3, "a")); list.add(new Dog(4L, "糖糖", 3, "A")); list.add(new Dog(6L, "文文", 3, "G")); list.add(new Dog(3L, "晴晴", 4, "g")); //提取組別號 轉換大寫 這裡可以看到 一個 Stream 只可以使用一次 map對映有很多方法 List<String> teamList = list.stream().map(Dog::getTeam).collect(Collectors.toList()); List<String> listCase = teamList.stream().map(String::toUpperCase).collect(Collectors.toList()); /**list操作*/ //1.獲取年齡最大/最小的狗 Dog dog2MaxAge = list.stream().max((d1, d2) -> d1.getAge().compareTo(d2.getAge())).orElse(new Dog()); Dog dog1MaxAge = list.stream().min(Comparator.comparing(Dog::getAge)).get(); //2.靜態方法 單一操作時候可以簡化為如下: list.stream().forEach(x -> System.out.println(x)); list.forEach(System.out::println); //3.排序 根據年齡排序 List<Dog> sortedAgeList = list.stream().sorted(Comparator.comparing(Dog::getAge)).collect(Collectors.toList()); //4.根據組隊號 自然排序 List<Dog> sortedTeamList = list.stream().sorted(Comparator.comparing(Dog::getTeam)).collect(Collectors.toList()); //5.根據名字的字母 Collator 類執行區分語言環境的 String 比較 List<Dog> sortedNameList = list.stream().sorted((o1, o2) -> Collator.getInstance(Locale.CHINESE).compare(o1.getName(), o2.getName())).collect(Collectors.toList()); //6.去重 去掉重複的結果 List<Dog> distinctList = list.stream().distinct().collect(Collectors.toList()); //7.擷取 擷取流的前N個元素 List<Dog> limitList = list.stream().limit(3).collect(Collectors.toList()); //8.跳過流的前n個元素 List<Dog> skipList = list.stream().skip(4).collect(Collectors.toList()); //9.將小流合併成一個大流 List<String> listLine = new ArrayList<String>(); listLine.add("I am a boy"); listLine.add("I love the girl"); listLine.add("But the girl loves another girl"); //按空格分詞 分完詞之後,每個元素變成了一個String[]陣列 Stream<String[]> stream = listLine.stream().map(line -> line.split(" ")); //將每個String[]變成流 Stream<Stream<String>> streamStream = listLine.stream().map(line -> line.split(" ")).map(Arrays::stream); //此時一個大流裡面包含了一個個小流,我們需要將這些小流合併成一個流 Stream<String> stringStream = listLine.stream().map(line -> line.split(" ")).flatMap(Arrays::stream); //規約 獲取年齡最大的狗狗 Dog dog = list.stream().reduce((p1, p2) -> { System.out.println("p1===" + p1); System.out.println("p2===" + p2); return p1.getAge() > p2.getAge() ? p1 : p2; }).get(); System.out.println(dog); //規約 求和 計算所有狗狗的年齡總和 //reduce的第一個引數表示初試值為0; //求和 那麼可以使用Integer提供了sum函式代替自定義的Lambda表示式 int age2Total = list.stream().mapToInt(Dog::getAge).reduce(0, Integer::sum); /**聚合運算*/ //求和 long sum = list.stream().mapToLong(Dog::getId).sum(); //均值 double average = list.stream().mapToInt(Dog::getAge).average().getAsDouble(); /**匹配:*/ //如果流為空,則返回false並且不評估謂詞 ==>檢查所有的名字是否是以 '文'結尾的 顯然不是 [少一個都不行,全檢性] 看電影去:<一個都不能少> boolean b1 = list.stream().allMatch(x -> x.getName().endsWith("文"));//false //如果流為空,則返回false並且不評估謂詞 ==>檢查是否有在A組的小狗 [存在性] boolean b2 = list.stream().anyMatch(x -> "A".compareToIgnoreCase(x.getTeam()) < 0);//true //如果流為空,則返回false並且不評估謂詞 ==>檢查是否有任意小狗 年齡大於3歲 [存在性] boolean b3 = list.stream().anyMatch(x -> x.getAge() < 3);//true // list = Collections.EMPTY_LIST; //如果流為空,則返回true並且不評估謂詞 ==> allMatch相反,檢查有麼狗的年齡大於100歲的 [有一個都不行,全檢性] boolean b4 = list.stream().noneMatch(x -> x.getAge() > 100);//true //findAny list即stream是有順序的 findAny能夠從流中隨便選一個元素出來 Optional<Dog> optionalDog = list.stream().findAny();//isPresent表示是否存在 System.out.println(optionalDog.isPresent()); //再看 其實就是 b6就是b7 boolean b6 = list.stream().filter(x -> x.getAge() < 3).findAny().isPresent();//true //過濾 List<Dog> filterList1 = list.stream().filter(x -> x.getAge() < 3).collect(Collectors.toList()); boolean b7 = list.stream().anyMatch(x -> x.getAge() < 3);//true 返回在流遍歷過程是否遇到過的年齡大於3的狗 //與guava的getFirst方法類似,Stream.findFirst將返回給定流的第一個元素,如果流為空,則返回空Optional。如果流沒有遭遇順序,則可以返回任何元素 Optional<Dog> first = list.stream().findFirst(); /** * parallelStream是什麼 * parallelStream其實就是一個並行執行的流.它通過預設的ForkJoinPool,可能提高你的多執行緒任務的速度. * Stream具有平行處理能力,處理的過程會分而治之,也就是將一個大任務切分成多個小任務,這表示每個任務都是一個操作,因此像以下的程式片段 * * 而可能是任意的順序,就forEach()這個操作來講,如果平行處理時,希望最後順序是按照原來Stream的資料順序,那可以呼叫forEachOrdered() * 如果forEachOrdered()中間有其他如filter()的中介操作,會試著平行化處理,然後最終forEachOrdered()會以原資料順序處理, * 因此,使用forEachOrdered()這類的有序處理,可能會(或完全失去)失去平行化的一些優勢,實際上中介操作亦有可能如此,例如sorted()方法 * * parallelStream背後的男人:ForkJoinPool * ForkJoinPool主要用來使用分治法(Divide-and-Conquer Algorithm)來解決問題 * 那麼使用ThreadPoolExecutor或者ForkJoinPool,會有什麼效能的差異呢? * 首先,使用ForkJoinPool能夠使用數量有限的執行緒來完成非常多的具有父子關係的任務,比如使用4個執行緒來完成超過200萬個任務。 * 但是,使用ThreadPoolExecutor時,是不可能完成的,因為ThreadPoolExecutor中的Thread無法選擇優先執行子任務, * 需要完成200萬個具有父子關係的任務時,也需要200萬個執行緒,顯然這是不可行的。 * */ List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); numbers.parallelStream().forEach(System.out::print); System.out.println("numbers.parallelStream()和numbers.stream().parallel()貌似一樣"); numbers.stream().parallel().forEach(System.out::print); System.out.println("numbers.parallelStream()和numbers.stream().parallel()貌似一樣"); numbers.stream().parallel().forEachOrdered(System.out::print); System.out.println(); Optional<Dog> any = list.parallelStream().findAny(); System.out.println(any.isPresent()); //並行流 此時的findAny哪一個都有可能返回 Optional<Dog> any1 = list.stream().parallel().findAny(); System.out.println(any1); /**map操作*/ //1.轉為Map 以編號:key 以名字為value 用::,和x->都可以 推薦:: Map<Long, String> map1 = list.stream().collect(Collectors.toMap(Dog::getId, x -> x.getName())); //2.轉為Map 以編號:key 以物件自身為value 用x->x,和Function.identity() 推薦::Function.identity() Map<Long, Dog> map2 = list.stream().collect(Collectors.toMap(Dog::getId, x -> x)); Map<Long, Dog> map3 = list.stream().collect(Collectors.toMap(Dog::getId, Function.identity())); //3.重複key的情況,這時候流的處理會丟擲個異常:Java.lang.IllegalStateException:Duplicate key。 //這時候就要在toMap方法中指定當key衝突時key的選擇。(這裡是選擇第二個key覆蓋第一個key) Map<Long, Dog> map4 = list.stream().collect(Collectors.toMap(Dog::getId, Function.identity(), (oldKey, newKey) -> newKey)); //4.根據 年齡or組隊 分組用groupingBy 可以分組成多個列表 Map<Integer, List<Dog>> map5 = list.stream().collect(Collectors.groupingBy(Dog::getAge)); Map<String, List<Dog>> map6 = list.stream().collect(Collectors.groupingBy(Dog::getName)); //5.partitioningBy可以理解為特殊的groupingBy,key值為true和false,當然此時方法中的引數為一個判斷語句(用於判斷的函式式介面) Map<Boolean, List<Dog>> map7 = list.stream().collect(Collectors.partitioningBy(o -> o.getAge() < 3)); //遍歷小map map6.forEach((k, v) -> System.out.println(k + v)); } }

 

 

 

 

 

 Collectors 類的靜態工廠方法:

工廠方法 返回型別 作用
toList List<T> 把流中所有專案收集到一個 List
toSet Set<T> 把流中所有專案收集到一個 Set,刪除重複項
toCollection Collection<T> 把流中所有專案收集到給定的供應源建立的集合menuStream.collect(toCollection(), ArrayList::new)
counting Long 計算流中元素的個數
sumInt Integer 對流中專案的一個整數屬性求和
averagingInt Double 計算流中專案 Integer 屬性的平均值
summarizingInt IntSummaryStatistics 收集關於流中專案 Integer 屬性的統計值,例如最大、最小、 總和與平均值
joining String 連線對流中每個專案呼叫 toString 方法所生成的字串collect(joining(", "))
maxBy Optional<T> 一個包裹了流中按照給定比較器選出的最大元素的 Optional, 或如果流為空則為 Optional.empty()
minBy Optional<T> 一個包裹了流中按照給定比較器選出的最小元素的 Optional, 或如果流為空則為 Optional.empty()
reducing 歸約操作產生的型別 從一個作為累加器的初始值開始,利用 BinaryOperator 與流 中的元素逐個結合,從而將流歸約為單個值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
collectingAndThen 轉換函式返回的型別 包裹另一個收集器,對其結果應用轉換函式int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size))
groupingBy Map<K, List<T>> 根據專案的一個屬性的值對流中的專案作問組,並將屬性值作 為結果 Map 的鍵
partitioningBy Map<Boolean,List<T>> 根據對流中每個專案應用謂詞的結果來對專案進行分割槽