Java8中有兩大最爲重要得改變,其一時Lambda表達式,另外就是 Stream API了。在前面幾篇中簡單學習了Lambda表達式得語法,以及函數式接口。本文就來簡單學習一下Stream API(java.util.stream.*)。java
Stream 是 Java8中處理集合得關鍵抽象概念,他能夠指定你但願對集合進行得操做,能夠執行很是複雜得查找、過濾和映射數據等操做。使用Stream API對集合數據進行操做,就相似使用SQL執行得數據庫查詢。也可使用S他ream API 來並行執行操做。簡而言之,Stream API 提供了一種高效且易於使用得處理數據得方式。數據庫
在Stream操做過程當中,能夠對數據流作過濾,排序,切片等操做,可是操做以後會產生一個新的流,而數據源則不會發生改變。api
1、什麼是 Stream數組
Stream是數據渠道,用於操做數據源(集合,數組等)所生成得元素序列。而集合講得是數據,流講得是計算。網絡
注意:dom
①. Stream 本身不會存儲元素。ide
②. Stream 不會改變源對象。相反,它會返回一個持有結果得新Stream函數
③. Stream 操做時延遲執行得,這意味着它們會等到須要結果時才執行。(延遲加載)學習
2、Stream 操做的三個步驟ui
1). 建立 Stream
一個數據源(集合,數組),獲取一個流。
2). 中間操做
一箇中間操做鏈,對數據源的數據進行處理。
3). 終止操做
一個終止操做,執行中間操做鏈,併產生結果。
3、建立Stream 的四種方式
1). 經過Collection得Stream()方法(串行流)或者 parallelStream()方法(並行流)建立Stream。
1 /** 2 * 建立 Stream的四種方式 3 * 1.經過Collection得Stream()方法(串行流) 4 或者 parallelStream()方法(並行流)建立Stream 5 */ 6 @Test 7 public void test1 () { 8 9 //1. 經過Collection得Stream()方法(串行流) 10 //或者 parallelStream()方法(並行流)建立Stream 11 List<String> list = new ArrayList<String>(); 12 Stream<String> stream1 = list.stream(); 13 14 Stream<String> stream2 = list.parallelStream(); 15 16 }
2).經過Arrays中得靜態方法stream()獲取數組流
1 /** 2 * 建立 Stream的四種方式 3 * 2. 經過Arrays中得靜態方法stream()獲取數組流 4 */ 5 @Test 6 public void test2 () { 7 8 //2. 經過Arrays中得靜態方法stream()獲取數組流 9 IntStream stream = Arrays.stream(new int[]{3,5}); 10 11 }
3). 經過Stream類中得 of()靜態方法獲取流
1 /** 2 * 建立 Stream的四種方式 3 * 3. 經過Stream類中得 of()靜態方法獲取流 4 */ 5 @Test 6 public void test3 () { 7 8 //3. 經過Stream類中得 of()靜態方法獲取流 9 Stream<String> stream = Stream.of("4645", "huinnj"); 10 11 }
4). 建立無限流(迭代、生成)
1 /** 2 * 建立 Stream的四種方式 3 * 4. 建立無限流(迭代、生成) 4 */ 5 @Test 6 public void test4 () { 7 8 //4. 建立無限流 9 //迭代(須要傳入一個種子,也就是起始值,而後傳入一個一元操做) 10 Stream<Integer> stream1 = Stream.iterate(2, (x) -> x * 2); 11 12 //生成(無限產生對象) 13 Stream<Double> stream2 = Stream.generate(() -> Math.random()); 14 15 }
4、Stream 中間操做
多箇中間操做能夠鏈接起來造成一個流水線,除非流水線上觸發終止操做,不然中間操做不會執行任何得處理!而終止操做時一次性所有處理,稱爲‘延遲加載’
1). 篩選與切片
①. filter —— 接收Lambda ,從流中排除某些元素。
1 /** 2 * 篩選與切片 3 * filter —— 接收Lambda ,從流中排除某些元素。 4 * 5 */ 6 @Test 7 public void test5 () { 8 //內部迭代:在此過程當中沒有進行過迭代,由Stream api進行迭代 9 //中間操做:不會執行任何操做 10 Stream<Person> stream = list.stream().filter((e) -> { 11 System.out.println("Stream API 中間操做"); 12 return e.getAge() > 30; 13 }); 14 15 //終止操做:只有執行終止操做纔會執行所有。即:延遲加載 16 stream.forEach(System.out :: println); 17 18 }
執行上面方法,獲得下面結果。
Person [name=張三, sex=男, age=76] Stream API 中間操做 Stream API 中間操做 Person [name=王五, sex=男, age=35] Stream API 中間操做 Stream API 中間操做 Person [name=錢七, sex=男, age=56] Stream API 中間操做 Person [name=翠花, sex=女, age=34]
咱們,在執行終止語句以後,一邊迭代,一邊打印,而咱們並無去迭代上面集合,其實這是內部迭代,由Stream API 完成。
下面咱們來看看外部迭代,也就是咱們人爲得迭代。
1 @Test 2 public void test6 () { 3 //外部迭代 4 Iterator<Person> it = list.iterator(); 5 while (it.hasNext()) { 6 System.out.println(it.next()); 7 } 8 9 }
②. limit —— 截斷流,使其元素不超過給定數量。
1 /** 2 * limit —— 截斷流,使其元素不超過給定數量。 3 */ 4 @Test 5 public void test7 () { 6 //過濾以後取2個值 7 list.stream().filter((e) -> e.getAge() >30 ). 8 limit(2).forEach(System.out :: println); 9 10 }
在這裏,咱們能夠配合其餘得中間操做,並截斷流,使咱們能夠取得相應個數得元素。並且在上面計算中,只要發現有2條符合條件得元素,則不會繼續往下迭代數據,能夠提升效率。
2). 跳過元素
skip(n),返回一個扔掉了前n個元素的流。若流中元素不足n個,則返回一個空,與limit(n)互補。
1 /** 2 * skip(n)—— 跳過元素,返回一個扔掉了前n個元素的流。 3 * 若流中元素不足n個,則返回一個空,與limit(n)互補。 4 */ 5 @Test 6 public void test8 () { 7 //跳過前2個值 8 list.stream().skip(2).forEach(System.out :: println); 9 10 } 11
3). 篩選
distinct 經過流所生成元素的hashCode()和equals()去除重複元素
1 /** 2 * distinct —— 篩選,經過流所生成元素的hashCode()和equals()去除重複元素 3 */ 4 @Test 5 public void test9 () { 6 7 list.stream().distinct().forEach(System.out :: println); 8 9 }
注意:distinct 須要實體中重寫hashCode()和 equals()方法纔可使用
4). 映射
① . map ,將元素轉換成其餘形式或者提取信息。接收一個函數做爲參數,該函數會被應用到每一個元素上,並將其映射成一個新的元素。
1 /** 2 * map —— 映射 ,將元素轉換成其餘形式或者提取信息。接收一個函數做爲參數,該函數會被應用到每一個元素上,並將其映射成一個新的元素。 3 */ 4 @Test 5 public void test10 () { 6 //將流中每個元素都映射到map的函數中,每一個元素執行這個函數,再返回 7 List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd"); 8 list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf); 9 10 //獲取Person中的每個人得名字name,再返回一個集合 11 List<String> names = this.list.stream().map(Person :: getName). 12 collect(Collectors.toList()); 13 }
② . flatMap —— 接收一個函數做爲參數,將流中的每一個值都換成一個流,而後把全部流鏈接成一個流
1 /** 2 * flatMap —— 接收一個函數做爲參數,將流中的每一個值都換成一個流,而後把全部流鏈接成一個流 3 */ 4 @Test 5 public void test11 () { 6 StreamAPI_Test s = new StreamAPI_Test(); 7 List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd"); 8 list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println); 9 10 //若是使用map則須要這樣寫 11 list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> { 12 e.forEach(System.out::println); 13 }); 14 } 15 16 /** 17 * 將一個字符串轉換爲流 18 * @param str 19 * @return 20 */ 21 public Stream<Character> filterCharacter(String str){ 22 List<Character> list = new ArrayList<>(); 23 for (Character ch : str.toCharArray()) { 24 list.add(ch); 25 } 26 return list.stream(); 27 }
其實map方法就至關於Collaction的add方法,若是add的是個集合得話就會變成二維數組,而flatMap 的話就至關於Collaction的addAll方法,參數若是是集合得話,只是將2個集合合併,而不是變成二維數組。
5). 排序
sorted有兩種方法,一種是不傳任何參數,叫天然排序,還有一種須要傳Comparator 接口參數,叫作定製排序。
1 /** 2 * sorted有兩種方法,一種是不傳任何參數,叫天然排序,還有一種須要傳Comparator 接口參數,叫作定製排序。 3 */ 4 @Test 5 public void test12 () { 6 // 天然排序 7 List<Person> persons = list.stream().sorted().collect(Collectors.toList()); 8 9 //定製排序 10 List<Person> persons1 = list.stream().sorted((e1, e2) -> { 11 if (e1.getAge() == e2.getAge()) { 12 return 0; 13 } else if (e1.getAge() > e2.getAge()) { 14 return 1; 15 } else { 16 return -1; 17 } 18 }).collect(Collectors.toList()); 19 }
5、Stream 終止操做
1). 查找與匹配
首先咱們先建立一個集合。
1 List<Person> persons = Arrays.asList( 2 new Person("張三", "男", 76, Status.FREE), 3 new Person("李四", "女", 12, Status.BUSY), 4 new Person("王五", "男", 35, Status.BUSY), 5 new Person("趙六", "男", 3, Status.FREE), 6 new Person("錢七", "男", 56, Status.BUSY), 7 new Person("翠花", "女", 34, Status.VOCATION), 8 new Person("翠花", "女", 34, Status.FREE), 9 new Person("翠花", "女", 34, Status.VOCATION) 10 );
①. allMatch —— 檢查是否匹配全部元素。
1 /** 2 * allMatch —— 檢查是否匹配全部元素。 3 * 判斷全部狀態是否都是FREE 4 */ 5 @Test 6 public void test13 () { 7 boolean b = persons.stream().allMatch((e) -> Status.FREE.equals(e.getStatus())); 8 System.out.println(b); 9 }
②. anyMatch —— 檢查是否至少匹配全部元素。
1 /** 2 * anyMatch —— 檢查是否至少匹配全部元素。 3 * 判斷是否有一個是FREE 4 */ 5 @Test 6 public void test14 () { 7 boolean b = persons.stream().anyMatch((e) -> Status.FREE.equals(e.getStatus())); 8 System.out.println(b); 9 }
③. noneMatch —— 檢查是否沒有匹配全部元素。
1 /** 2 * noneMatch —— 檢查是否沒有匹配全部元素。 3 * 判斷是否沒有FREE 4 */ 5 @Test 6 public void test15 () { 7 boolean b = persons.stream().noneMatch((e) -> Status.FREE.equals(e.getStatus())); 8 System.out.println(b); 9 }
④. findFirst —— 返回第一個元素。
1 /** 2 * findFirst —— 返回第一個元素。 3 * 4 */ 5 @Test 6 public void test16 () { 7 Optional<Person> person = persons.stream().findFirst(); 8 System.out.println(person); 9 10 person.orElse(new Person("王五", "男", 35, Status.BUSY)); 11 }
注意:上面findFirst 返回的是一個Optional的對像,他將咱們的Person封裝了一層,這是爲了不空指針。並且這個對象爲咱們提供了一個orElse方法,就是當咱們獲得的這個對象爲空時,咱們能夠傳入一個新得對象去替代它。
⑤. findAny —— 返回當前流中任意元素。
1 /** 2 * findAny —— 返回當前流中任意元素。 3 */ 4 @Test 5 public void test17 () { 6 Optional<Person> person = persons.stream().findAny(); 7 System.out.println(person); 8 9 person.orElse(new Person("王五", "男", 35, Status.BUSY)); 10 }
⑥. count —— 返回流中元素總個數。
1 /** 2 * count —— 返回流中元素總個數。 3 */ 4 @Test 5 public void test18 () { 6 long count = persons.stream().count(); 7 System.out.println(count); 8 9 }
⑦. max —— 返回流中最大值。
1 /** 2 * max —— 返回流中最大值。 3 */ 4 @Test 5 public void test18 () { 6 Optional<Person> person = persons.stream().max((e1, e2) -> Double.compare(e1.getAge(), e2.getAge())); 7 System.out.println(person); 8 9 }
⑧. min —— 返回流中最小值。
1 /** 2 * min —— 返回流中最小值。 3 */ 4 @Test 5 public void test20 () { 6 Optional<Person> person = persons.stream().min((e1, e2) -> Double.compare(e1.getAge(), e2.getAge())); 7 System.out.println(person); 8 9 }
2). 歸約(能夠將流中元素反覆結合在一塊兒,獲得一個值)
①. reduce(T identitty,BinaryOperator)首先,須要傳一個起始值,而後,傳入的是一個二元運算。
1 /** 2 * reduce(T identitty,BinaryOperator)首先,須要傳一個起始值,而後,傳入的是一個二元運算。 3 */ 4 @Test 5 public void test21 () { 6 List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 7 Integer sum = list.stream().reduce(0, (x, y) -> x + y); 8 }
②. reduce(BinaryOperator)此方法相對於上面方法來講,沒有起始值,則有可能結果爲空,因此返回的值會被封裝到Optional中。
1 /** 2 * reduce(BinaryOperator)此方法相對於上面方法來講,沒有起始值,則有可能結果爲空,因此返回的值會被封裝到Optional中 3 */ 4 @Test 5 public void test22 () { 6 List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 7 Optional<Integer> sum = list.stream().reduce(Integer :: sum); 8 }
備註:map和reduce的鏈接一般稱爲map-reduce模式,因Google用它來進行網絡搜索而出名。
3). 收集collect(將流轉換爲其餘形式。接收一個Collector接口得實現,用於給其餘Stream中元素作彙總的方法)
Collector接口中方法得實現決定了如何對流執行收集操做(如收集到List,Set,Map)。可是Collectors實用類提供了不少靜態方法,能夠方便地建立常見得收集器實例。
①. Collectors.toList() 將流轉換成List
1 /** 2 * Collectors.toList() 將流轉換成List 3 */ 4 @Test 5 public void test23() { 6 7 List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList()); 8 }
②. Collectors.toSet()將流轉換爲Set
1 /** 2 * Collectors.toSet() 將流轉換成Set 3 */ 4 @Test 5 public void test24() { 6 7 Set<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toSet()); 8 }
③. Collectors.toCollection()將流轉換爲其餘類型的集合
1 /** 2 * Collectors.toCollection()將流轉換爲其餘類型的集合 3 */ 4 @Test 5 public void test25() { 6 7 LinkedList<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toCollection(LinkedList :: new)); 8 }
④. Collectors.counting() 元素個數
1 /** 2 * Collectors.counting() 總數 3 */ 4 @Test 5 public void test26() { 6 List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 7 Long count = list.stream().collect(Collectors.counting()); 8 }
⑤. Collectors.averagingDouble()、Collectors.averagingDouble()、Collectors.averagingLong() 平均數,這三個方法均可以求平均數,不一樣之處在於傳入得參數類型不一樣,返回值都爲Double
1 /** 2 * Collectors.averagingInt() 、 3 * Collectors.averagingDouble()、 4 * Collectors.averagingLong() 平均數, 5 * 者三個方法均可以求平均數,不一樣之處在於傳入得參數類型不一樣, 6 * 返回值都爲Double 7 */ 8 @Test 9 public void test27() { 10 List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 11 Double avg = list.stream().collect(Collectors.averagingInt((x) -> x)); 12 }
⑥. Collectors.summingDouble()、Collectors.summingDouble()、Collectors.summingLong() 求和,不一樣之處在於傳入得參數類型不一樣,返回值爲Integer, Double, Long
1 /** 2 * Collectors.summingInt() 、 3 * Collectors.summingDouble()、 4 * Collectors.summingLong() 求和, 5 * 者三個方法均可以求總數,不一樣之處在於傳入得參數類型不一樣, 6 * 返回值爲Integer, Double, Long 7 */ 8 @Test 9 public void test28() { 10 List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 11 Integer sum = list.stream().collect(Collectors.summingInt((x) -> x)); 12 }
⑦. Collectors.maxBy() 求最大值
1 /** 2 * Collectors.maxBy() 求最大值 3 */ 4 @Test 5 public void test29() { 6 List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 7 Optional<Integer> max = list.stream().collect(Collectors.maxBy((x, y) ->Integer.compare(x, y))); 8 }
⑧. Collectors.minBy() 求最小值
/** * Collectors.minBy() 求最小值 */ @Test public void test29() { List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Optional<Integer> min = list.stream().collect(Collectors.minBy((x, y) ->Integer.compare(x, y))); }
⑨. Collectors.groupingBy()分組 ,返回一個map
1 /** 2 * Collectors.groupingBy()分組 ,返回一個map 3 */ 4 @Test 5 public void test30() { 6 Map<String, List<Person>> personMap = list.stream().collect(Collectors.groupingBy(Person :: getSex)); 7 }
Collectors.groupingBy()還能夠實現多級分組
1 /** 2 * Collectors.groupingBy()多級分組 ,返回一個map 3 */ 4 @Test 5 public void test31() { 6 Map<String, Map<Status, List<Person>>> personMap = list.stream().collect(Collectors.groupingBy(Person :: getSex, Collectors.groupingBy(Person :: getStatus))); 7 }
⑩. Collectors.partitioningBy() 分區,參數中傳一個函數,返回true,和false 分紅兩個區
1 /** 2 * Collectors.partitioningBy() 分區,參數中傳一個函數,返回true,和false 分紅兩個區 3 */ 4 @Test 5 public void test32() { 6 Map<Boolean, List<Person>> personMap = list.stream().collect(Collectors.partitioningBy((x) -> x.getAge() > 30)); 7 }
上面就是Stream的一些基本操做,只要勤加練習就能夠靈活使用,並且效率大大提升。