在上一篇中咱們帶領你們簡單的瞭解流的概念及使用場景,知道了流的本質操做是將外部迭代轉爲了內部迭代,由此程序併發性上獲得了很大的優化。不過咱們在面對複雜的需求是又如何經過流來操做呢?在本節咱們就來好好的介紹流的常見用法,以此來處理複雜數據,以及流的其餘類型:數值流、文件流以及無限流等內容。java
對於一串流,咱們有時須要取出咱們須要的流中某些元素,例如:符合某些條件的元素、只須要流中的某一段等。主要是經過謂詞篩選。數組
一、咱們如何去除流中重複的元素,以及什麼樣的元素是重複元素呢?流中提供了一個distinct方法來實現。併發
二、如何取出流中某一段長度的流,流中提供了一個limit方法來知足咱們的需求。dom
三、若咱們須要跳過流中的某一段,又該如何作呢?一樣Java8中也提供了一個skip方法來知足咱們跳過固定長度的流。ide
下面咱們帶着這三個疑問及提示用代碼來講明問題。優化
首先定義一個POJO,後續操做都基於此來實例化元素。this
1 package com.hz; 2 3 /** 4 * 民警實體類 5 */ 6 public class Police { 7 /** 8 * 民警警號 9 */ 10 private String policeNo; 11 /** 12 * 民警姓名 13 */ 14 private String policeName; 15 /** 16 * 民警年齡 17 */ 18 private Integer policeAge; 19 /** 20 * 是否退休 21 */ 22 private boolean policeIsRetire; 23 24 public Police(String policeNo, String policeName, Integer policeAge, boolean policeIsRetire) { 25 this.policeNo = policeNo; 26 this.policeName = policeName; 27 this.policeAge = policeAge; 28 this.policeIsRetire = policeIsRetire; 29 } 30 31 public String getPoliceNo() { 32 return policeNo; 33 } 34 35 public void setPoliceNo(String policeNo) { 36 this.policeNo = policeNo; 37 } 38 39 public String getPoliceName() { 40 return policeName; 41 } 42 43 public void setPoliceName(String policeName) { 44 this.policeName = policeName; 45 } 46 47 public Integer getPoliceAge() { 48 return policeAge; 49 } 50 51 public void setPoliceAge(Integer policeAge) { 52 this.policeAge = policeAge; 53 } 54 55 public boolean isPoliceIsRetire() { 56 return policeIsRetire; 57 } 58 59 public void setPoliceIsRetire(boolean policeIsRetire) { 60 this.policeIsRetire = policeIsRetire; 61 } 62 63 @Override 64 public String toString() { 65 return "Police{" + 66 "policeNo='" + policeNo + '\'' + 67 ", policeName='" + policeName + '\'' + 68 ", policeAge=" + policeAge + 69 ", policeIsRetire='" + policeIsRetire + '\'' + 70 '}'; 71 } 72 73 @Override 74 public boolean equals(Object o) { 75 if (this == o) return true; 76 if (!(o instanceof Police)) return false; 77 78 Police police = (Police) o; 79 80 if (policeIsRetire != police.policeIsRetire) return false; 81 if (!policeNo.equals(police.policeNo)) return false; 82 if (!policeName.equals(police.policeName)) return false; 83 return policeAge.equals(police.policeAge); 84 } 85 86 @Override 87 public int hashCode() { 88 int result = policeNo.hashCode(); 89 result = 31 * result + policeName.hashCode(); 90 result = 31 * result + policeAge.hashCode(); 91 result = 31 * result + (policeIsRetire ? 1 : 0); 92 return result; 93 } 94 }
1 package com.hz; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 import static java.util.stream.Collectors.toList; 7 8 /** 9 * 篩選某些元素 10 * 包含:filter / distinct / limit / skip 11 */ 12 public class FilterAndLimitStreamDemo { 13 public static void main(String[] args) { 14 List<Police> policeList = Arrays.asList( 15 new Police("P001", "餘警官", 27, false), 16 new Police("P002", "李警官", 32, false), 17 new Police("P003", "程警官", 25, false), 18 new Police("P004", "楊警官", 35, false), 19 new Police("P005", "張警官", 70, true), 20 new Police("P006", "王警官", 68, true), 21 new Police("P007", "趙警官", 77, true), 22 new Police("P008", "劉警官", 64, true), 23 new Police("P008", "劉警官", 64, true) 24 ); 25 26 //***** 1-使用謂詞篩選 27 List<Police> filterPolices = policeList.stream().filter(Police::isPoliceIsRetire).collect(toList()); 28 System.out.println("結果1: " + filterPolices); 29 30 System.out.println("---------------- 分割線 ---------------------"); 31 32 //***** 2-篩選 大於60歲 並 去除重複的數據(是否重複 根據HashCode和equal決定 二者同時) 33 policeList.stream().filter(p -> p.getPoliceAge() > 60).distinct().forEach(System.out :: println); 34 35 System.out.println("---------------- 分割線 ---------------------"); 36 37 //***** 3-截流 截取前三位退休的民警 38 policeList.stream().filter(Police :: isPoliceIsRetire).limit(3).forEach(System.out :: println); 39 40 System.out.println("---------------- 分割線 ---------------------"); 41 42 //***** 4-跳過幾個元素 獲取退休民警 並跳過前兩位退休民警 43 policeList.stream().filter(Police :: isPoliceIsRetire).skip(2).forEach(System.out :: println); 44 } 45 }
映射即將流中的元素進行處理後返回新的流,即從某些對象中選擇信息。在SQL語言中,咱們一般會取出表中的某個字段信息,將其返回給頁面展現,若在Java語言中,如何實現一樣的功能呢?語言也提供了兩個方法讓咱們來實現。spa
一、map:該方法會對流中的每一個元素進行處理,咱們能夠理解爲將流中的每一個元素進行轉換後返回一個新的流。例如咱們如何取出全部民警的姓名。code
二、flatMap:既然有了map方法,Java又爲何提供一個flatMap呢?此爲流的扁平化。咱們以前講解過,小的流和組成一個大的流,若咱們有個大的流,大的流中有多少小的流咱們並不知道,此時咱們沒法對每一個小的流再次進行處理,而flatMap方法便可解決咱們的問題,它能夠將大流中的小流的值組成一個新的大流,以後再對該流進行元素處理。例如咱們須要將多個單詞重複的字母給去除,此時每一個單詞便可組裝爲一個小流,若咱們使用map來處理,只會將每一個單詞本身的重複字母給去除,兩個單詞之間的重複字母沒法去除。對象
1 package com.hz; 2 3 import java.util.Arrays; 4 import java.util.List; 5 import java.util.stream.Stream; 6 7 import static java.util.stream.Collectors.toList; 8 9 /** 10 * 流中映射 11 */ 12 public class StreamMapDemo { 13 public static void main(String[] args) { 14 List<Police> policeList = Arrays.asList( 15 new Police("P001", "餘警官", 27, false), 16 new Police("P002", "李警官", 32, false), 17 new Police("P003", "程警官", 25, false), 18 new Police("P004", "楊警官", 35, false), 19 new Police("P005", "張警官", 70, true), 20 new Police("P006", "司馬警官", 68, true), 21 new Police("P007", "趙科", 77, true), 22 new Police("P008", "劉警官", 64, true), 23 new Police("P008", "劉警官", 64, true) 24 ); 25 26 //***** 1-對流元素中的字段進行處理 獲取民警的姓名 和 獲取民警姓名長度 27 policeList.stream().map(Police :: getPoliceName).forEach(System.out :: println); 28 29 List<Integer> policesNameLength = policeList.stream().map(Police::getPoliceName).map(String::length).collect(toList()); 30 System.out.println("結果: " + policesNameLength); 31 32 System.out.println("------------ 分割線 ----------------"); 33 34 //***** 2-流的扁平化 將一組單詞 重複的字母去除 35 String[] words = {"gong", "zi", "chuan", "qi"}; 36 Stream<String> wordsStream = Arrays.stream(words); //將數組轉爲流對象 37 Stream<String> wordsStream2 = Arrays.stream(words); //將數組轉爲流對象 38 39 //此時爲 將每一個單詞爲一個數組轉爲一個流即四個流 流自己沒有重複 兩個流之間是存在重複的 40 List<Stream<String>> streamsList = wordsStream.map(s -> s.split("")).map(Arrays::stream).distinct().collect(toList()); 41 System.out.println(streamsList); 42 //wordsStream爲一個流 不可屢次使用 43 // java8中有個扁平化的處理 爲將流中的值組成一個大的流 flatMap 44 List<String> disWordsList = wordsStream2.map(s -> s.split("")).flatMap(Arrays::stream).distinct().collect(toList()); 45 System.out.println("結果:" + disWordsList); 46 47 System.out.println("---------------- 分割線 --------------------"); 48 49 //***** 3-一個實例 給定兩個數字列表,如何返回全部的數對呢? 50 // 例如,給定列表[1, 2, 3]和列表[3, 4],應該返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)] 51 Integer[] ints1 = {1, 2, 3}; 52 Integer[] ints2 = {3, 4}; 53 54 // List<List<int[]>> result = Arrays.stream(ints1).map(i -> { 55 // List<int[]> tempList = Arrays.stream(ints2).map(j -> { 56 //// int[] temp = new int[2]; 57 //// temp[0] = i; 58 //// temp[1] = j; 59 //// return temp; 60 // return new int[]{i, j}; 61 // }).collect(toList()); 62 // return tempList; 63 // }).collect(toList()); 64 65 //更簡化 66 List<List<int[]>> result = Arrays.stream(ints1).map(i -> Arrays.stream(ints2).map(j -> new int[]{i, j}).collect(toList())).collect(toList()); 67 68 result.forEach(l -> l.forEach(is -> System.out.println((is[0] + "," + is[1])))); 69 } 70 }
有時咱們須要查看數據集中的某些元素是否匹配一個給定的屬性。對於該需求,Java中分別提供了 allMatch 、 anyMatch 、 noneMatch 、 findFirst 和 findAny 方法來知足咱們。
一、allMatch:該方法表示流中的全部元素都知足咱們的條件。例如咱們想知道當前的民警容器中是否是全部的民警都爲退休民警。
二、anyMatch:該方法表示流中的元素至少有一個知足咱們的條件。例如咱們想知道有沒有民警退休了。
三、noneMatch:表示全部的元素都不知足。它與allMatch正好相反。
四、findFirst:在流中找出第一個元素,該方法咱們通常會配合篩選來使用。
五、findAny:在流中隨意找出一個元素,通常狀況下方法返回給咱們的都爲第一個元素。
1 package com.hz; 2 3 import java.util.*; 4 5 /** 6 * 流中查找和匹配 7 */ 8 public class FindAndMatchDemo { 9 public static void main(String[] args) { 10 List<Police> policeList = Arrays.asList( 11 new Police("P001", "餘警官", 27, false), 12 new Police("P002", "李警官", 32, false), 13 new Police("P003", "程警官", 25, false), 14 new Police("P004", "楊警官", 35, false), 15 new Police("P005", "張警官", 70, true), 16 new Police("P006", "司馬警官", 68, true), 17 new Police("P007", "趙科", 77, true), 18 new Police("P008", "劉警官", 64, true), 19 new Police("P008", "劉警官", 64, true) 20 ); 21 22 //***** 1-檢查流中元素至少匹配一個 民警列表中至少有一名退休民警 23 System.out.println("--------------- anyMatch分割線 ------------------"); 24 if (policeList.stream().anyMatch(Police :: isPoliceIsRetire)) { 25 System.out.println("列表存在退休民警..."); 26 } else { 27 System.out.println("當前沒有退休民警..."); 28 } 29 30 System.out.println("----------- allMatch分割線 ------------"); 31 32 //***** 2-檢查流中是否匹配全部元素 是否爲退休民警列表 33 if (policeList.stream().allMatch(Police :: isPoliceIsRetire)) { 34 System.out.println("該列表爲一個退休民警列表..."); 35 } else { 36 System.out.println("該列表中存在未退休的民警..."); 37 } 38 39 System.out.println("------------- noneMatch分割線 -----------"); 40 //與allMatch對應的是noneMatch 即全部都不匹配 41 if (policeList.stream().noneMatch(Police::isPoliceIsRetire)) { 42 System.out.println("該列表都不是退休民警"); 43 } else { 44 System.out.println("該列表存在退休民警"); 45 } 46 47 System.out.println("--------------- 查找分割線 --------------------"); 48 49 //***** 3-查找元素 從民警列表中找出任一元素 50 Optional<Police> anyPolice = policeList.stream().findAny(); 51 System.out.println(anyPolice); 52 //在這裏Optional這是一個容器類,該容器中只能存儲一個對象 其中Optional中實現了一些方法用來操做和判斷該容器是否有值 53 //這裏咱們簡單瞭解下,詳細後面咱們在好好介紹下該類 54 //***** 案例:找出任意一個退休民警 並打印該民警姓名 ifPresent如有值則執行 55 policeList.stream().filter(Police :: isPoliceIsRetire).findAny().ifPresent(p -> System.out.println(p.getPoliceName())); 56 boolean hasValue = policeList.stream().filter(Police::isPoliceIsRetire).findAny().isPresent(); //是否有值isPresent 57 System.out.println("容器中是否有匹配的值:" + hasValue); 58 // 若容器中有值則返回,不然返回一個默認的值 59 Police police = policeList.stream().filter(Police :: isPoliceIsRetire).findAny().orElse(new Police("","",0, false)); 60 System.out.println("返回民警: " + police); 61 //獲取匹配的民警 有則返回 沒有則異常java.util.NoSuchElementException: No value present 62 Police police1 = policeList.stream().filter(Police::isPoliceIsRetire).findAny().get(); 63 System.out.println("獲取民警: " + police1); 64 65 System.out.println("---------------"); 66 //***** 4-查找流中第一個元素 67 Police police2 = policeList.stream().filter(Police::isPoliceIsRetire).findFirst().get(); 68 System.out.println("結果: " + police2); 69 } 70 }
以上提供的方法,咱們會發現返回給咱們的都是一個boolean類型、無返回或爲一個容器,但是咱們有時須要將流中的每一個元素從新組合來獲得結果。這就須要將流中全部的元素結合着來處理,此時就須要歸約。
一、例如將流中全部民警的年齡求和。固然使用for-each能夠實現,可在這裏咱們還有更好的方式嗎?
二、咱們須要當前監獄年齡最大的民警和年齡最小的民警。此時又該如何實現呢?咱們總不至於要將全部的民警挨個遍歷一篇吧!
以上兩個問題其實都是能夠經過歸約獲得很好的解決,除此之外,咱們會發現年齡都是與數值相關的,這也將引出咱們普通流的變種,即數值流。下面以代碼來講明問題:
package com.hz; import java.util.Arrays; import java.util.List; import java.util.Optional; public class ReduceDemo { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(5, 4, 3, 9); //***** 1-將一組數值求和 Integer sum = numbers.stream().reduce(0, (a, b) -> a + b); System.out.println("sum: " + sum); //若reduce未指定第一個參數,則不知實際返回類型,此時語言自己返回一個Optional容器對象 Optional<Integer> sumOptional = numbers.stream().reduce((a, b) -> a + b); System.out.println("sum-optional: " + sumOptional); //變化 sum = numbers.stream().reduce(0, Integer :: sum); System.out.println("sum2: " + sum); System.out.println("------------ 分割線 --------------"); //***** 2-獲取一組數的最大和最小值 Optional<Integer> max = numbers.stream().reduce(Integer::max); System.out.println("max: " + max); Optional<Integer> min = numbers.stream().reduce(Integer :: min); System.out.println("min: " + min); System.out.println("---------- 分割線 --------"); //**** 3-一個案例:使用map和reduce統計民警列表中有多少民警 List<Police> policeList = Arrays.asList( new Police("P001", "餘警官", 27, false), new Police("P002", "李警官", 32, false), new Police("P003", "程警官", 25, false), new Police("P004", "楊警官", 35, false), new Police("P005", "張警官", 70, true), new Police("P006", "司馬警官", 68, true), new Police("P007", "趙科", 77, true), new Police("P008", "劉警官", 64, true) ); Integer policeNum = policeList.stream().map(p -> 1).reduce(0, Integer::sum); System.out.println("列表中民警數量: " + policeNum); } }
關於流的這麼多操做,咱們上面都說了不少,可是在實際需求中咱們應該如何的靈活運用呢?下面咱們經過一些問題案例來總結一下流運用。
(1) 找出2019年入職的民警,並按年齡排序(從低到高)。
(2) 民警籍貫都在哪裏?
(3) 查找全部來自於浙江的民警,並按姓名排序。
(4) 返回全部民警的警號,並按警號順序排序。
(5) 有沒有民警籍貫是在安徽的?
(6) 獲取籍貫爲浙江的民警的年齡之和
(7) 全部民警中,年齡最大的是多少?
(8) 找到民警中年齡最小的民警
package com.hz; import java.util.*; /** * 關於流操做的一個案例 */ public class StreamDemo { public static void main(String[] args) { List<Police> polices = Arrays.asList( new Police("P001", "Y警官", 27, "浙江", 2019), new Police("P002", "L警官", 32, "安徽", 2019), new Police("P003", "C警官", 25, "安徽", 2015), new Police("P004", "Y警官", 35, "浙江", 2015), new Police("P005", "Z警官", 31, "上海", 2018), new Police("P006", "W警官", 42, "浙江", 2018), new Police("P007", "Z警官", 31, "浙江", 2019), new Police("P009", "Z警官", 32, "浙江", 2019), new Police("P008", "L警官", 49, "浙江", 2019) ); // (1) 找出2019年入職的民警,並按年齡排序(從低到高)。 polices.stream().filter(p -> p.getPoliceEntryYear() == 2019).sorted(Comparator.comparing(Police::getPoliceAge)).forEach(System.out::println); System.out.println("---------------- 分割線 --------------------------"); // (2) 民警籍貫都在哪裏? polices.stream().map(Police::getPoliceNativePlace).distinct().forEach(System.out::println); System.out.println("---------------- 分割線 --------------------------"); // (3) 查找全部來自於浙江的民警,並按姓名排序。 polices.stream().filter(p -> "浙江".equals(p.getPoliceNativePlace())).sorted(Comparator.comparing(Police::getPoliceName)).forEach(System.out::println); System.out.println("---------------- 分割線 --------------------------"); // (4) 返回全部民警的警號,並按警號順序排序。 polices.stream().map(Police::getPoliceNo).sorted().forEach(System.out::println); System.out.println("---------------- 分割線 --------------------------"); // (5) 有沒有民警籍貫是在安徽的? if (polices.stream().anyMatch(p -> "安徽".equals(p.getPoliceNativePlace()))) { System.out.println("存在籍貫爲安徽的民警..."); } else { System.out.println("不存在籍貫爲安徽的民警..."); } System.out.println("---------------- 分割線 --------------------------"); // (6) 獲取籍貫爲浙江的民警的年齡之和 Integer ageSum = polices.stream().filter(p -> "浙江".equals(p.getPoliceNativePlace())).map(Police::getPoliceAge).reduce(0, Integer::sum); //以上方式暗中存在一個裝箱操做 其實stream中有個數值流能夠省去這個隱藏操做 ageSum = polices.stream().filter(p -> "浙江".equals(p.getPoliceNativePlace())).mapToInt(Police::getPoliceAge).sum(); System.out.println("全部浙江民警年齡總和:" + ageSum); System.out.println("---------------- 分割線 --------------------------"); // (7) 全部民警中,年齡最大的是多少? Optional<Integer> ageMaxOptional = polices.stream().map(Police::getPoliceAge).reduce(Integer::max); // 或使用流的max方法 ageMaxOptional = polices.stream().max(Comparator.comparing(Police::getPoliceAge)).map(Police::getPoliceAge); //一樣 該方式也能夠轉爲數值流計算 OptionalInt maxAgeOp = polices.stream().mapToInt(Police::getPoliceAge).max(); System.out.println(maxAgeOp.getAsInt()); if (ageMaxOptional.isPresent()) { System.out.println("全部民警最大年齡爲: " + ageMaxOptional.get()); } else { System.out.println("沒有民警..."); } System.out.println("---------------- 分割線 --------------------------"); // (8) 找到民警中年齡最小的民警 Optional<Police> ageMinPoliceOptional = polices.stream().reduce((p1, p2) -> p1.getPoliceAge() < p2.getPoliceAge() ? p1 : p2); if (ageMinPoliceOptional.isPresent()) { System.out.println("年齡最小的民警: " + ageMinPoliceOptional); } else { System.out.println("列表中沒有民警..."); } //其實還有更加簡單的方式,流中有個min方法 ageMinPoliceOptional = polices.stream().min(Comparator.comparing(Police::getPoliceAge)); System.out.println(ageMinPoliceOptional); } static class Police { private String policeNo; private String policeName; private Integer policeAge; private Integer policeEntryYear; private String policeNativePlace; public Police(String policeNo, String policeName, Integer policeAge, String policeNativePlace, Integer policeEntryYear) { this.policeNo = policeNo; this.policeName = policeName; this.policeAge = policeAge; this.policeNativePlace = policeNativePlace; this.policeEntryYear = policeEntryYear; } public String getPoliceNo() { return policeNo; } public void setPoliceNo(String policeNo) { this.policeNo = policeNo; } public String getPoliceName() { return policeName; } public void setPoliceName(String policeName) { this.policeName = policeName; } public Integer getPoliceAge() { return policeAge; } public void setPoliceAge(Integer policeAge) { this.policeAge = policeAge; } public String getPoliceNativePlace() { return policeNativePlace; } public void setPoliceNativePlace(String policeNativePlace) { this.policeNativePlace = policeNativePlace; } public Integer getPoliceEntryYear() { return policeEntryYear; } public void setPoliceEntryYear(Integer policeEntryYear) { this.policeEntryYear = policeEntryYear; } @Override public String toString() { return "Police{" + "policeNo='" + policeNo + '\'' + ", policeName='" + policeName + '\'' + ", policeAge=" + policeAge + ", policeEntryYear='" + policeEntryYear + '\'' + ", policeNativePlace='" + policeNativePlace + '\'' + '}'; } } }
關於Java中流的介紹,咱們也說明的差很少了,流對於表達數據處理查詢是很是強大而有用的。不過在上面咱們對流的建立都是基於集合以後作集合轉換,但是除了集合以外,咱們還有其餘建立流的方式嗎?答案是確定的。關於數值流咱們在上節實例中已經作了簡單說明,在流的建立過程當中,有時咱們並不知道流的內容,此時須要根據變化來建立流對象。
1 package com.hz; 2 3 import java.io.IOException; 4 import java.nio.charset.Charset; 5 import java.nio.file.Files; 6 import java.nio.file.Paths; 7 import java.util.ArrayList; 8 import java.util.Arrays; 9 import java.util.List; 10 import java.util.stream.Stream; 11 12 /** 13 * 咱們以前建立的流都是經過一個List對象或數組 14 * 其餘方式也是能夠建立流的 15 */ 16 public class CreateStreamDemo { 17 public static void main(String[] args) { 18 // 1-List建立流 19 List<String> list = new ArrayList<>(); 20 Stream<String> stream = list.stream(); 21 22 // 2-數組建立流 23 String[] strings = {"Gong", "Zi", "Qi"}; 24 Stream<String> stream1 = Arrays.stream(strings); 25 26 // 3-靜態方法建立流 27 Stream<String> stream2 = Stream.of("Gong", "Zi", "Qi"); 28 Stream.iterate(0, i -> i + 2).limit(10).forEach(System.out::println); 29 Stream<Object> stream4 = Stream.generate(() -> "GongZiQi"); 30 //說明: iterate 和 generate 在生成流是須要注意截取,它們均可以作無限流的生成 以下 31 // Stream.iterate(0, i -> i + 2).forEach(System.out::println); //生成全部偶數 注意 32 // Stream.generate(Math::random).forEach(System.out::println); //生成全部隨機數 0~1之間 注意 33 34 // 4-文件生成流 35 try { 36 Stream<String> stream3 = Files.lines(Paths.get("data.txt"), Charset.defaultCharset()); 37 } catch (IOException e) { 38 e.printStackTrace(); 39 } 40 } 41 }