<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # Stream流 [TOC] ## 導學 Stream流的使用主要針對集合和數組等數據容器,Stream流的作用包括對數據容器進行過濾,映射,去重,排序等轉換操作,以及將Stream流轉為集合或統計等聚合操作。 所以Stream流的出現,就是為了彌補jdk中集合類庫的缺陷。以下有些Stream流的使用注意點 1. Stream流不是一種數據結構,不保存數據,它只是在原數據集上定義了一組操作。 2. 這些操作是惰性的,即每當訪問到流中的一個元素,才會在此元素上執行這一系列操作。 3. Stream不保存數據,故每個Stream流只能使用一次。 ~~~ //stream針對于集合進行操作 List<String> list = new ArrayList<>(); list.add("red"); list.add("orange"); list.add("red"); list.add("blue"); list.add("white"); list.add("black"); list.add("gray"); list.add("red"); list.add("green"); Stream<String> sm1 = list.stream();//Stream<String>和List<String>只是長得像,但Stream不會作為一個數據容器。 Stream<String> sm2 = sm1.distinct();//由sm1轉化為sm2流對象的時候,并不會對集合中的元素產生任何的操作 Stream<String> sm3 = sm2.map(str -> str += "123"); List<String> lst = sm3.collect(Collectors.toList());//只有在對sm3流對象進行聚合時。才會真正執行元素聚合操作 //List<String> lst = list.stream().distinct().map(str -> str += "123").collect(Collectors.toList()); System.out.println(list); System.out.println(lst); ~~~ **關于Stream流的使用策略** 1. 首先需要構建Stream流 2. 每次操作Stream流,不會改變原有的Stream流對象,只會生成新的Stream流對象,而且可以把執行聚合任務之前的Stream流對象認為是用來記錄轉換操作的。 3. Stream流中很多方法都會返回新Stream流,所以Stream流編程大多是鏈式編程風格 4. 最后,在操作完Stream流后,基本會對Stream流進行聚合操作(執行真正的計算,并返回新集合等內容) 5. 在執行完聚合操作之后,Stream流就會被關閉,所以對同一個Stream流而言,轉換操作可以執行多次, 但聚合操作只能執行一次。 ## Stream流的使用 ### Stream流對象的創建 Stream流對象的創建主要有兩大類,第一種是基于Stream流本身的靜態方法,第二種基于數組和集合的`stream()`方法。 1. Stream中的靜態方法 1.1 `generate()`方法和`iterate()`方法 ![](https://img.kancloud.cn/2e/47/2e472a18720a2da8815a72bfe5b119bb_1442x109.png) 這兩個方法都需要自定義元素的生成方式,所以使用的還是比較少的。 ~~~ public class Test { public static void main(String[] args) { Stream<Integer> sm = Stream.generate(new Add()); // 注意:無限序列必須先變成有限序列再打印: sm.limit(20).forEach(System.out::println); } } class Add implements Supplier<Integer> { int n = 0; public Integer get() { n++; return n; } } ~~~ 1.2 `of()`方法 ![](https://img.kancloud.cn/53/db/53dbbaa84c7c5aa4e86b32fc063e77cb_1446x109.png) 在Stream中,存在兩個重載的靜態`of()`方法,這兩個方法,一個接收任意類型的可變參數列表作為參數,另一個接收單個任意類型參數。 ~~~ public class StreamTest { public static void main(String[] args) { Stream<String> s = Stream.of("1","2","3","4"); Stream<String> s1 = Stream.of("2"); } } ~~~ 2. 數組和集合的`stream()`方法 在日常的使用中,Stream主要用于集合和數組操作,所以這里的兩個stream方法也是用的比較多的。 ~~~ public class StreamTest { public static void main(String[] args) { String[] arr = new String[]{"a","b","c"}; Stream<String> s = Arrays.stream(arr); List<String> list = new ArrayList<>(); Stream<String> s1 = list.stream(); } } ~~~ ### Stream流中的轉換操作 Stream流中的轉換操作,其實是用來記錄Stream對元素的轉換操作,本質上并不涉及對元素的直接實質化操作,僅僅是起一個記錄的作用,與之相對的是聚合操作。轉換操作通常會由一個Stream流對象,返回一個新的Stream流對象,以下是一些常用的Stream對象的常用方法。 * **`forEach()`方法** `forEach()`方法比較特殊,該方法雖然是一個轉換操作,但該方法沒有返回值。 ~~~ public class StreamTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); //asList方法生成的集合不可改動,所以將該集合的元素添加到另一個集合中 list.addAll(Arrays.asList(new String[]{"red","blue","green","yellow","red","black"})); list.stream().forEach(System.out::println); } } ~~~ * **`map()`方法** `map()`:將對Stream序列中的每一個元素進行操作,參數為Function接口對象,要求傳入的是對元素的執行內容,在map中,Function接口對象的apply方法的參數的類型是Stream流中,元素的類型,因為該方法接收的是Stream流中的元素。 ~~~ public class StreamTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.addAll(Arrays.asList(new String[]{"red","blue","green","yellow","red","black"})); list.stream().map(str -> str += "顏色").forEach(System.out::println); } } ~~~ 問:執行完map方法后,新生成的stream對象中,元素的個數有沒有變化?元素的類型有沒有變化? 答:**元素個數不變,因為map只操作元素,不刪除元素。元素類型有可能變化。** * **`flatMap()`方法** `flatMap()`方法針對原有Stream流對象中的數據容器進行扁平化操作,將每個數據容器轉換為小的Stream對象,最終將所有小的Stream流對象合并為新的大的Stream流對象。 ~~~ public class StreamTest { public static void main(String[] args) { List<String[]> list = new ArrayList<>(); list.addAll(Arrays.asList(new String[][]{{"red","blue"},{"green","yellow","red","black"}})); list.stream().flatMap(arr -> Arrays.stream(arr)).forEach(System.out::println); } } ~~~ * **`filter()`方法** `filter()`方法將對Stream序列中的每一個元素進行過濾,參數為Predicate接口對象,要求傳入的是對元素的過濾規則,在filter中,Predicate接口對象中存在test方法,該方法接收stream中的元素作為參數,返回布爾類型的值。如果test方法返回為true,則stream流中的元素得以保存,如果返回為false,則清除該元素。 ~~~ public class StreamTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.addAll(Arrays.asList(new String[]{"red","blue","green","yellow","red","black"})); list.stream().filter(str -> !"red".equals(str)).forEach(System.out::println); } } ~~~ 問:執行完filter方法后,新生成的stream對象中,元素的個數有沒有變化?元素的類型有沒有變化? 答:**元素個數可能變化,因為filter會執行清除元素操作。元素類型沒有變化。** * **`sorted()`方法** `sorted()`方法對stream流中的元素進行排序,建議使用`Collections.sort()` * **`distinct()`方法** `distinct()`方法用于去除重復的元素,對于重復元素的判斷,調用的是equals方法。該方法無需參數 * **`skip()`方法 & `limit()`方法** `skip()`方法用于跳過當前Stream的前N個元素。 `limit()`方法用于截取當前Stream最多前N個元素,兩個方法都是接收int類型參數。 ~~~ public class StreamTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.addAll(Arrays.asList(new String[]{"red","blue","green","yellow","red","black"})); list.stream().skip(3).map(str -> str += 1).forEach(System.out::println); System.out.println("===================="); list.stream().limit(3).map(str -> str += 1).forEach(System.out::println); } } ~~~ 思考題:如何只針對Stream流中,除首尾之外的其他元素進行處理? * **`concat()`方法** `concat()`方法將兩個泛型相同的stream流對象,合并為一個stream流對象 * **`allMatch()`方法 & `anyMatch()`方法** 這兩個方法用于測試元素是否都滿足條件/是否有一個滿足條件,這兩個方法的返回值都是Boolean類型。 ~~~ public class StreamTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.addAll(Arrays.asList(new String[]{"red","blue","green","yellow","red","black"})); System.out.println(list.stream().anyMatch(str -> "red".equals(str))); System.out.println(list.stream().allMatch(str -> "red".equals(str))); } } ~~~ ### 聚合操作 Stream流中的聚合操作,實際上用來真正執行元素的計算任務的,或是可以用來對聚合完的元素進行整合輸出。在Stream流中,有很多的聚合操作,在這里我們主要挑出常用的兩大類聚合操作來說明: * **`reduce()`方法** `reduce()`方法可以用來對Stream中的元素進行聚合,我們可以用`reduce()`方法來進行元素的求和求積等操作,還可以進行一些轉換元素類型的高級操作。`reduce()`方法本身具有三種重載方法: ![](https://img.kancloud.cn/56/59/5659f8e26f48d911ce2e822a171d05df_1439x159.png) **以下,通過一個問題來學習`reduce()`方法,問,如何將Person集合中,各元素的人名取出來組成一個姓名集合?** * 單參`reduce()` 單參`reduce()`方法,返回值為Optional類型,這是因為在不給定默認值的情況下,Stream本身并不會去確定`reduce()`方法的運行結果,需要調用者自己去確定。 ~~~ public class StreamTest { public static void main(String[] args) { List<Person> list = new ArrayList<>(); list.add(new Person("張三",23)); list.add(new Person("李四",23)); list.add(new Person("王五",23)); //Optional類是一個容器類,它可以保存模板類型T的值,也可以保存null。 Optional<List<String>> lt = list.stream().map(person -> { List<String> lst = new ArrayList<>(); lst.add(person.name); return lst; }).reduce((lst1, lst2) -> { lst1.addAll(lst2); return lst1; }); if(lt.isPresent()) { List<String> ls = lt.get(); System.out.println(ls); } } } class Person { public String name; public int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } } ~~~ 在上述代碼中,我們在`Stream`流中,先將三個`Person`類型的數據,轉變為三個`List<String>`類型的數據,然后在將數據使用`reduce()`整合為一個`List`。但是此時并沒有指定默認值,所以此時可能出現結果為null的情況,所以`reduce()`方法將返回值設置為可以存儲null的`Optional`容器。 參考資料:**Java8新特性 Optional的正確使用姿勢** https://www.jianshu.com/p/59db4b2c7543 * 雙參`reduce()` 對于雙參的方法,我們在使用前,給定了默認值,這個默認值需要和Stream中的元素是同一類型的 ~~~ public class StreamTest { public static void main(String[] args) { List<Person> list = new ArrayList<>(); list.add(new Person("張三",23)); list.add(new Person("李四",23)); list.add(new Person("王五",23)); List<String> lt = list.stream().map(person -> { List<String> lst = new ArrayList<>(); lst.add(person.name); return lst; }).reduce(new ArrayList<String>(), (lst1, lst2) -> { lst1.addAll(lst2); return lst1; }); System.out.println(lt); } } class Person { public String name; public int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } } ~~~ * 三參`reduce()` 實際上,在前兩個方法的使用中我們發現,`reduce()`方法有著很多的使用限制,包括返回值類型需要和元素類型相同等問題,實際上,限制了我們對Stream中元素的進一步修改。在三參方法中,第三個參數是一個BinaryOperator類型的表達式。在常規情況下我們可以忽略這個參數,隨便指定一個表達式即可,目的是為了通過編譯器的檢查,因為在常規的Stream中它并不會被執行到,然而, 雖然此表達式形同虛設,可是我們也不是把它設置為null,否則還是會報錯。 在并行Stream中,此表達式則會被執行到。 ~~~ public class StreamTest { public static void main(String[] args) { List<Person> list = new ArrayList<>(); list.add(new Person("張三",23)); list.add(new Person("李四",23)); list.add(new Person("王五",23)); List<String> lt = list.stream().map(person -> person.name).reduce(new ArrayList<String>(), (lst, per) -> { lst.add(per); return lst; }, (lst, per) -> null); System.out.println(lt); } } class Person { public String name; public int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } } ~~~ 那么,我們可以看到使用三參的方法,使我們代碼少寫很多,同樣也顯得優雅很多。那是否可以以一種更優雅的方式呢? * **`collect()`方法** collect()`方法有兩個重載方法,但用的最多的還是和`java.util.stream.Collectors`類共同使用的方法。比如我們去改寫上面問題的代碼 ~~~ List<String> lt = list.stream().map(person -> person.name).collect(Collectors.toList()); ~~~ 這樣的代碼同樣可以去實現`reduce()`方法中一樣的效果,其中主要依靠的就是`Collectors`類中自帶方法的作用。 * **`toCollection()`方法** 該方法用于將流中的元素全部放置到一個集合中返回,包括List,Set,Queue ~~~ public class StreamTest { public static void main(String[] args) { List<Person> list = new ArrayList<>(); list.add(new Person("張三",23)); list.add(new Person("李四",23)); list.add(new Person("王五",23)); list.add(new Person("王五",23)); //List<String> lt = list.stream().map(person -> person.name).collect(Collectors.toCollection(ArrayList::new)); Set<String> lt = list.stream().map(person -> person.name).collect(Collectors.toCollection(HashSet::new)); System.out.println(lt); } } class Person { public String name; public int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } } ~~~ * **`toList()`方法 & `toSet()`方法** 這兩個方法用于明確將Stream流聚合為List集合或Set集合 ~~~ List<String> lt = list.stream().map(person -> person.name).collect(Collectors.toList()); Set<String> lt = list.stream().map(person -> person.name).collect(Collectors.toSet()); ~~~ * **`joining()`方法** 用于拼接流中的元素,如果元素不是String類型,將會以String類型的形式被拼接,該方法可以指定分隔符。 ~~~ String lt = list.stream().map(person -> person.name).collect(Collectors.joining(",")); ~~~ * **`mapping()`方法** 用于對流中所有的元素執行轉換,該方法接收兩個參數,第一個參數為Function接口,將會執行其中的apply抽象方法,作為元素轉換的執行內容。第二個參數為Collector接口的實現類,該實現類可以指定元素的聚合。 `mapping()`方法其實和`map()`方法有些類似,都是用于對元素進行映射操作。但不同的地方在于`mapping()`方法有兩個參數。第一個參數用于表示對元素的轉換等操作, 第二個參數一般用于表示對元素的聚合操作,所以該方法也屬于聚合作用。 ~~~ public class StreamTest { public static void main(String[] args) { List<Person> list = new ArrayList<>(); list.add(new Person("張三",23)); list.add(new Person("李四",23)); list.add(new Person("王五",23)); list.add(new Person("王五",23)); //List<String> lt = list.stream().map(per -> per.name).collect(Collectors.toList()); List<String> lt = list.stream().collect(Collectors.mapping(per -> per.name, Collectors.toList())); System.out.println(lt); } } ~~~ 其實從上述代碼可以看出,在`mapping()`方法中,我是把原有的map方法和collect方法結合起來了,那是否就意味著mapping方法有點多此一舉呢?其實不是這樣,mapping方法多用于在Stream轉換完成后,需要最終進行轉換并聚合的情況。 * **`collectingAndThen()`方法** 用于對聚合完的數據,進行再次聚合,其實第二次聚合可以認為是已聚合好數據的一種轉換。 ~~~ int lt = list.stream().map(per -> per.name).collect(Collectors.collectingAndThen(Collectors.toList(), lst -> lst.size())); ~~~ * **`counting()`方法** 用于對聚合完的數據進行統計總個數 ~~~ long lt = list.stream().map(per -> per.name).collect(Collectors.counting()); ~~~ * **`summingInt()`方法 & `summingLong()`方法 & `summingDouble()`方法** 這幾個方法,用于對流中的元素進行求和,該方法的參數作用為將流傳過來的參數改為對應的類型 ~~~ //int lt = list.stream().map(per -> per.age).collect(Collectors.summingInt(Integer::valueOf)); long lt = list.stream().map(per -> per.age).collect(Collectors.summingLong(Long::valueOf)); ~~~ `averagingInt`/`averagingLong`/`averagingDouble`:求平均數,使用方法同上 * **`toMap()`方法** 該方法用于將Stream中的元素轉為Map集合,一般而言,我們會盡量使用三參的方法,如果確定key值不會重復,可以使用兩參的方法。 在三參方法中,第一個參數用于生成key值,第二個參數用于生成value值,第三個參數用于提供key值相同時的解決方案。 ![](https://img.kancloud.cn/46/17/46173b3c3ba394893c2eba6b48f20ee1_1446x182.png) ~~~ Map<String, Integer> lt = list.stream().collect(Collectors.toMap(per -> per.name, per -> per.age, (k1, k2) -> k2)); ~~~ * **`groupingBy()`方法** 該方法用于對Stream流的元素進行分組,該方法存在重載方法,主要使用的是單參方法,只需要傳入分組依據即可。 ~~~ Map<Object, List<Person>> lt = list.stream().collect(Collectors.groupingBy(per -> per.age)); ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看