我們一起來學java8的lambda表達式Stream
阿新 • • 發佈:2018-12-03
all span 優先執行 collector 一起 chinese 跳過 匹配 多線程
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; importlombok.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>> |
根據對流中每個項目應用謂詞的結果來對項目進行分區 |
我們一起來學java8的lambda表達式Stream