Java SE 8 的流庫學習筆記

前言:流提供了一種讓咱們能夠在比集合更高的概念級別上指定計算的數據視圖。如:java

//使用foreach迭代
long count = 0;
for (String w : words) {
    if (w.length () > 6) count++;
}

//使用流
long count = words.stream ()
     .filter (w -> w.length () > 6 )
     .count ();

流的版本更易於閱讀,流遵循了「作什麼而非怎麼作」的原則。算法

1、什麼是流數組

  Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。原始版本的 Iterator,用戶只能顯式地一個一個遍歷元素並對其執行某些操做;高級版本的 Stream,用戶只要給出須要對其包含的元素執行什麼操做,好比 「過濾掉長度大於 10 的字符串」、「獲取每一個字符串的首字母」等,Stream 會隱式地在內部進行遍歷,作出相應的數據轉換。數據結構

  Stream 就如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次後即用盡了,就比如流水從面前流過,一去不復返。併發

  而和迭代器又不一樣的是,Stream 能夠並行化操做,迭代器只能命令式地、串行化操做。顧名思義,當使用串行方式去遍歷時,每一個 item 讀完後再讀下一個 item。而使用並行去遍歷時,數據會被分紅多個段,其中每個都在不一樣的線程中處理,而後將結果一塊兒輸出。Stream 的並行操做依賴於 Java7 中引入的 Fork/Join 框架(JSR166y)來拆分任務和加速處理過程。app

  Stream 的另一大特色是,數據源自己能夠是無限的。框架

  流表面上和集合很相似,均可以讓咱們轉換和獲取數據,但它們之間存在顯著的差別:dom

一、流並不存儲其元素。這些元素可能存儲在底層的集合中,或者按需生成的。ide

二、流的操做不會修改其數據源,例如,filter方法不會重新的流中移除元素,而是會生成一個新的流,其中不包含過濾掉的元素。函數

三、流的操做是儘量惰性執行的。這意味着直至須要其結果時,操做纔會執行。

2、流的構成

  如Demo01所示,這個工做流是操做流時的典型流程,咱們創建了一個包含三個階段的操做管道:

一、建立一個流;

二、指定初始流轉換爲其餘流的中間操做,可能包含多個步驟;

三、應用終止操做,從而產生結果。這個操做會強制執行以前的惰性操做,今後以後這個流就不再能用了。

 1 package chapter01;
 2 import java.io.IOException;
 3 import java.nio.charset.StandardCharsets;
 4 import java.nio.file.Files;
 5 import java.nio.file.Paths;
 6 import java.util.Arrays;
 7 import java.util.List;
 8 
 9 public class Demo01 {
10     public static void main(String[] args) throws IOException {
11         String contents = new String (Files.readAllBytes (
12                 Paths.get ("alice.txt")), StandardCharsets.UTF_8);
13         List<String> words = Arrays.asList (contents.split ("\\PL+"));//以非字母爲分隔符
14 
15         long count = 0;
16         for (String w : words) {
17             if (w.length () > 10) count++;
18         }
19         System.out.println (count);
20 
21         /*long count = words.stream ()
22                 .filter (w -> w.length () > 6 )
23                 .count ();
24         System.out.println (count);*/
25         long count1 = words.parallelStream ()
26                 .filter (w -> w.length () > 10 )
27                 .count ();
28         System.out.println (count1);
29     }
30 }

3、流的操做

  流的操做類型分爲兩種:

  • Intermediate(轉換操做):一個流能夠後面跟隨零個或多個 intermediate 操做。其目的主要是打開流,作出某種程度的數據映射/過濾,而後返回一個新的流,交給下一個操做使用。這類操做都是惰性化的(lazy),就是說,僅僅調用到這類方法,並無真正開始流的遍歷。
  • Terminal(終止操做):一個流只能有一個 terminal 操做,當這個操做執行後,流就被使用「光」了,沒法再被操做。因此這一定是流的最後一個操做。Terminal 操做的執行,纔會真正開始流的遍歷,而且會生成一個結果,或者一個 side effect。

4、流的建立

  • 經過集合類中的stream() 、 parallelStream()的方法建立;
  • 經過數組的Arrays.stream(Object[])建立;
  • 靜態工廠方法的流類,如Stream.of(Object[]) 、IntStream.range(int, int)Stream.iterate(Object, UnaryOperator);
  • 能夠從BufferedReader.lines()建立;
  • 能夠經過Files類中的流文件路徑方法建立;
  • 流的隨機數字能夠從Random.ints()建立;
  • 許多其餘stream-bearing JDK中的方法,包括BitSet.stream()、Pattern.splitAsStream(java.lang.CharSequence)、JarFile.stream()

  更多的流源可使用這些技術的第三方庫提供。

  1 package chapter01;
  2 
  3 import java.io.IOException;
  4 import java.math.BigInteger;
  5 import java.nio.charset.StandardCharsets;
  6 import java.nio.file.Files;
  7 import java.nio.file.Path;
  8 import java.nio.file.Paths;
  9 import java.util.Arrays;
 10 import java.util.List;
 11 import java.util.regex.Pattern;
 12 import java.util.stream.Collectors;
 13 import java.util.stream.Stream;
 14 
 15 public class CreatingStreams {
 16     public static <T> void show(String title, Stream<T> stream){
 17         final int SIZE = 10;
 18         List<T> firstElements = stream
 19                 .limit (SIZE+1)
 20                 .collect(Collectors.toList());
 21         System.out.print (title + ":");
 22         for (int i = 0; i < firstElements.size (); i++) {
 23             if (i > 0) System.out.print (",");
 24             if (i < SIZE ) System.out.print (firstElements.get (i));
 25             else System.out.print ("...");
 26         }
 27         System.out.println ();
 28     }
 29     public static void main(String[] args) throws IOException {
 30         Path path = Paths.get ("alice.txt");
 31         String contents = new String (Files.readAllBytes (path),
 32                 StandardCharsets.UTF_8);
 33 
 34         /**
 35          * static <T> Stream<T> of(T... values)
 36          * 產生一個元素爲指定值的流
 37          */
 38         Stream<String> words = Stream.of (contents.split ("\\PL+"));//以標點符號爲分隔符
 39         show ("words",words);
 40         //words:Now,you,don,t,have,to,scan,the,loop,for,...
 41         Stream<String> song = Stream.of ("gently","down","the","stream");
 42         show ("song",song);
 43         //song:gently,down,the,stream
 44 
 45         /**
 46          * static <T> Stream<T> empty()
 47          * 產生一個不包含任何元素的流
 48          */
 49         Stream<String> silence = Stream.empty ();
 50         show ("silence",silence);
 51         //silence:
 52 
 53         /**
 54          * static <T> Stream<T> generate(Supplier<T> s)
 55          * 產生一個無限流,它的值是經過反覆調用函數s而構建的
 56          */
 57         Stream<String> echos = Stream.generate (() -> "Echo");
 58         show ("echos",echos);
 59         //echos:Echo,Echo,Echo,Echo,Echo,Echo,Echo,Echo,Echo,Echo,...
 60 
 61         Stream<Double> randoms = Stream.generate (Math::random);
 62         show ("randoms",randoms);
 63         //randoms:0.2080635816548484,0.6166016438525503,0.16543339415969027,
 64         // 0.3650300855876488,0.9934670157259281,0.6335908473779187,0.9586911195597475,
 65         // 0.2948129537037052,0.06688403354835959,0.958008821429481,...
 66 
 67         /**
 68          * static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
 69          * 產生一個無限流,它的元素包含種子、在種子上調用f產生的值、在前一個元素上調用f產生的值,等等
 70          */
 71         Stream<BigInteger> integers = Stream.iterate (BigInteger.ONE,
 72                 n -> n.add (BigInteger.ONE));
 73         show ("integers",integers);
 74         //integers:1,2,3,4,5,6,7,8,9,10,...
 75 
 76         /**
 77          * static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive)
 78          * 產生一個流,它的元素是數組中指定範圍內的元素構成的。
 79          */
 80         String[] strings = {"A","B","C","D","E"};
 81         Stream<String> stringStream = Arrays.stream (strings,0,3);
 82         show ("stringStream",stringStream);
 83         //stringStream:A,B,C
 84 
 85         /**
 86          * Stream<String> spiltAsStream(CharSequence input)
 87          * 產生一個流,它的元素是輸入中由該模式界定的部分
 88          */
 89         Stream<String> wordsAnotherWay = Pattern.compile ("\\PL+").splitAsStream (contents);
 90         show ("wordsAnotherWay",wordsAnotherWay);
 91         //wordsAnotherWay:Now,you,don,t,have,to,scan,the,loop,for,...
 92 
 93         /**
 94          * java.nio.file.Files 7
 95          * static Stream<String> lines(Path path)
 96          * static Stream<String> lines(Path path,Charset cs)
 97          * 產生一個流,它的元素是指定文件中的行,該文件的字符集爲UTF-8,或者爲指定的字符集
 98          */
 99         try (Stream<String> lines = Files.lines (path,StandardCharsets.UTF_8)){
100             show ("lines",lines);
101             //lines:Now you don’t have to scan the loop for evidence of filtering and counting.
102             // ,The method names tell you right away what the code intends to do.
103             // ,Moreover, where the loop prescribes the order of operations in complete detail,
104             // ,a stream is able to schedule the operations any way it wants, as long as the result is correct.
105             // ,Specify intermediate operations for transforming the initial stream into others, possibly in multiple steps.
106         }
107     }
108 }

5、處理流的轉換

   流的轉換會產生一個新的流,它的元素派生自另一個流中的元素。

  一、filter、map、flagMap方法

  ①Stream<T> filter(Predicate<? super T> predicate)

  產生一個流,它包含當前流中全部知足斷言條件的元素。如將一個字符串流轉換爲了只含長單詞的另外一個流:

List<String> wordList = ...;

Stream<String> longWords = wordList.stream().filter(w -> w.length() > 12);

  ②<R> Stream<R> map(Function<? super T,? extends R> mapper) 

  產生一個流,它包含將mapper應用於當前流中全部元素所產生的結果。如:將全部單詞都轉換爲小寫:

Stream<String> lowercaseWords = words.stream().map(String::toLowerCase);

  ③<R> Stream<R> flatMap(Function<? super T,? extends R> mapper)

  產生一個流,它經過將mapper應用於當前流中全部元素所產生的結果鏈接到一塊兒而得到的。(這裏的每一個結果都是一個流。)

  二、抽取子流和鏈接流

  ①Stream<T> limit(long maxSize)

  產生一個流,其中包含了當前流中最初的maxSize個元素(若是原來的流更短,那麼就會在流結束時結束)。這個方法對於裁剪無限流的尺寸會顯得特別有用,如:產生一個包含100個隨機數的流

Stream<Double> randoms = Stream.generate(Math::random).limit(100);

  ②Stream<T> skip(long n)

  產生一個流,它的元素是當前流中除了前n個元素以外的全部元素。

  ③static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

  產生一個流,它的元素是a的元素後面跟着b的元素。

  三、其餘的轉換流

  ①Stream<T> distinct()

  產生一個流,包含當前流中全部不一樣的元素。

Stream<String> uniqueWords
= Stream.of ("merrily","merrily","merrily","gently").distinct ();
//Only one "merrily" is retained

  ② Stream<T> sorted()

   Stream<T> sorted(Comparator<? super T> comparator)

   產生一個流,它的元素是當前流中的全部元素按照順序排列的。第一方法要求元素實現了Comparable的類的實例。

Stream<String> longestFirst = 

  words.stream().sorted(Comparator.comparing(String::length).reversed());

//長字符排在前

  ③Stream<T> peek(Consumer<? super T> action)

  產生一個流,它與當前流中的元素相同,在獲取其中每一個元素是,會將其傳遞給action。如:

Object[] powers = Stream.iterate (1.0,p -> p*2)
.peek (e -> System.out.println ("Fetching" + e))
.limit (20).toArray ();

  當實際訪問一個元素時,就會打印出一條消息。經過這種方式,你能夠驗證iterate返回的無限流是被惰性處理的。

  對應調試,你可讓peek調用一個你設置了斷點的方法。

6、簡單約簡

  約簡是一種終結操做(terminal operation),它們會將流約簡爲能夠在程序中使用的非流值。

Optional<T> max(Comparator<? super T> comparator)

Optional<T> min(Comparator<? super T> comparator)

  分別產生這個流的最大元素和最小元素,使用由給定比較器定義的排序規則,若是這流爲空,會產生一個空的Optional對象。這些操做都是終結操做。

Optional<T> findFirst()

Optional<T> findAny()

  分別產生這個流的第一個元素和任意一個元素,若是這流爲空,會產生一個空的Optional對象。這些操做都是終結操做。

boolean anyMatch(Predicate<? super T> predicate)

boolean allMatch(Predicate<? super T> predicate)

boolean noneMatch(Predicate<? super T> predicate)

  分別在這個流中任意元素、全部元素和沒有任何元素匹配給定端言時返回true,這些操做都是終結操做。

7、Optional類型

  Optional<T> 對象是一種包裝器對象,要麼包裝了類型T的對象,要麼沒有包裝任何對象。

  一、如何使用Optional值

 策略一:在沒有任何匹配時,使用某種默認值,能夠是空字符串。

T orElse(T other)

如:String result = optionalString.orElse("");//The wrapped string,or "" if none

  產生這個Optional的值,或者在該Optional爲空時,產生other。

T orElseGet(Supplier<? extends T> other)

如:String result = optionalString.orElseGet(() -> Locale.getDefault().getDisplayName());

  //The function is only called when needed

  產生這個Optional的值,或者在該Optional爲空時,產生調用other的結果。

<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)

如:String result = optionalString.orElseThrow(IllegalStateException::new);

  //Supply a method that yields an exception object

  產生這個Optional的值,或者在該Optional爲空時,拋出調用exceptionSupplier的結果。

 策略二:只有在其存在的狀況下才消費該值

void ifPresent(Consumer<? super T> consumer)

如:optionalValue.ifPresent(results::add);

  若是該Optional不爲空,那麼就將它的值傳遞給consumer。

<U> Optional <U> map(Function<? super T,? extends U> mapper)

如:Optional<Boolean> added = optionalValue.map(results::add);

  產生將該Optional的值傳遞給mapper後的結果,只要這個Optional不爲空且結果不爲null,不然產生一個空Optional。

  二、不適合使用Optional值的方式

T get()

  產生這個Option的值,或者在該Optional爲空時,拋出一個NoSuchElementException對象。

boolean isPresent()

  若是該Optional不爲空,則返回true。

  三、建立Optional

static <T> Optional<T> of(T value)

static <T> Optional<T> ofNullable(T value)

  產生一個具備給定值的Optional。若是value爲null,那麼第一個方法會拋出一個NullPointerException對象,而第二方法會產生一個空Optional。

static <T> Optional<T> empty()

  產生一個空Optional。 

public static Optional<Double> inverse(Double x){
       return x == 0 ? Optional.empty() : Optional.of(1 / x);  
}

  ofNullable(obj)方法被用來做爲可能出現的null值和可選值之間的橋樑。

  四、用flatMap來構建Optional值的函數

  <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper)

  產生將mapper應用於當前的Optional值所產生的結果,或者在當前Optional爲空時,返回一個空Optional。  

 1 import java.io.IOException;
 2 import java.nio.charset.StandardCharsets;
 3 import java.nio.file.Files;
 4 import java.nio.file.Paths;
 5 import java.util.*;
 6 
 7  
 8 public class OptionalTest {
 9     public static void main(String[] args) throws IOException {
10         String contents = new String (Files.readAllBytes (
11                 Paths.get ("alice.txt")), StandardCharsets.UTF_8);
12         List<String> wordList = Arrays.asList (contents.split ("\\PL+"));
13 
14         Optional<String> optionalValue = wordList.stream ()
15                 .filter (s -> s.contains ("filter"))
16                 .findFirst ();
17         System.out.println (optionalValue.orElse ("No word")+" contains filter");
18         //filtering contains filter
19 
20         Optional<String> optionalString  = Optional.empty ();
21         String result = optionalString.orElse ("N/A");
22         System.out.println ("result:"+result);
23         //result:N/A
24         result = optionalString.orElseGet (() -> Locale.getDefault ().getDisplayName ());
25         System.out.println ("result:"+result);
26         //result:English (China)
27 
28         try {
29             result = optionalString.orElseThrow (IllegalStateException::new);
30             System.out.println ("result:"+result);
31         } catch (Throwable t){
32             t.printStackTrace ();
33         }
34        /* java.lang.IllegalStateException
35         at java.base/java.util.Optional.orElseThrow(Optional.java:385)
36         at chapter01.OptionalTest.main(OptionalTest.java:32)*/
37 
38         optionalValue = wordList.stream ()
39                 .filter (s -> s.contains ("met"))
40                 .findFirst ();
41         optionalValue.ifPresent (s -> System.out.println (s+" contains met"));
42         //method contains met
43         Set<String> results = new HashSet<String> ();
44         optionalValue.ifPresent (results::add);
45         Optional<Boolean> added = optionalValue.map (results::add);
46         System.out.println (added);
47         //Optional[false]
48 
49         System.out.println (inverse(4.0).flatMap(OptionalTest::squareRoot));
50         //Optional[0.5]
51         System.out.println (inverse(-1.0).flatMap(OptionalTest::squareRoot));
52         //Optional.empty
53         System.out.println (inverse(0.0).flatMap(OptionalTest::squareRoot));
54         //Optional.empty
55         Optional<Double> result2 = Optional.of (-4.0)
56                 .flatMap (OptionalTest::inverse).flatMap (OptionalTest::squareRoot);
57         System.out.println (result2);
58         //Optional.empty
59     }
60 
61 
62     private static Optional<Double> inverse(Double x) {
63         return x == 0 ? Optional.empty () : Optional.of (1 / x ) ;
64     }
65 
66     private static Optional<Double> squareRoot(Double x) {
67         return x < 0 ? Optional.empty () : Optional.of (Math.sqrt (x));
68     }
69 
70 }

8、收集結果

  1 import java.io.IOException;
  2 import java.nio.charset.StandardCharsets;
  3 import java.nio.file.Files;
  4 import java.nio.file.Paths;
  5 import java.util.*;
  6 import java.util.stream.Collectors;
  7 import java.util.stream.Stream;
  8  
  9 public class CollectingResult {
 10     public static Stream<String> noVowels() throws IOException {
 11         String contents = new String (Files.readAllBytes (
 12                 Paths.get ("alice.txt")), StandardCharsets.UTF_8);
 13         List<String> wordList = Arrays.asList (contents.split ("\\PL+"));
 14         Stream<String> words = wordList.stream ();
 15         return words.map (s -> s.replaceAll ("[aeiouAEIOU]",""));
 16     }
 17     public static <T> void show(String label,Set<T> set){
 18         System.out.print(label + ":" + set.getClass ().getName ());
 19         System.out.println ("["
 20             + set.stream ().limit (10).map (Object::toString)
 21             .collect (Collectors.joining (","))+"]");
 22     }
 23 
 24     public static void main(String[] args) throws IOException {
 25         /**
 26          * java.util.stream.BaseStream
 27          * Iterator<T> iterate()
 28          * 產生一個用於獲取當前流中各個元素的迭代器。這是一個終結操做。
 29          */
 30         Iterator<Integer> iter = Stream.iterate (0,n -> n+1).limit (10)
 31                 .iterator ();
 32         while (iter.hasNext ())
 33             System.out.println (iter.next ());
 34         /**
 35          *Object[] toArray()
 36          *<A> A[] toArray(IntFunction<A[]> generator)
 37          * 產生一個對象數組,或者在將引用A[]::new傳遞給構造器時,返回一個A類型的數組,這些操做都是終結操做。
 38          */
 39         Object[] numbers = Stream.iterate (0, n -> n+1).limit (10).toArray ();
 40         System.out.println ("Object array:"+numbers);//Note it's an Object[] array
 41 
 42         try{
 43             Integer number = (Integer) numbers[0];
 44             System.out.println ("number:" + number);
 45             System.out.println ("The following statement throws an exception:");
 46             Integer[] number2 = (Integer[]) numbers;//Throws exception
 47         } catch (ClassCastException ex){
 48             System.out.println (ex);
 49         }
 50         Integer[] number3 = Stream.iterate (0,n -> n+1).limit (10)
 51                 .toArray (Integer[]::new);
 52         System.out.println ("Integer array:" + number3); //Note it's an Integer[] array
 53 
 54         /**
 55          * java.util.stream.Stream
 56          * <R,A> R collect(Collector<? super T,A,R> collector)
 57          * 使用給定的收集器來收集當前流中的元素。Collectors類有用於多種收集器的工廠方法。
 58          */
 59         /**
 60          * java.util.stream.Collectors
 61          * static <T> Collectors<T,?,List<T>> toList()
 62          * static <T> Collectors<T,?,Set<T>> toSet()
 63          * 產生一個將元素收集到列表或集中的收集器
 64          */
 65         Set<String> noVowelSet = noVowels ()
 66                 .collect(Collectors.toSet());
 67         show ("noVowelSet",noVowelSet);
 68         /**
 69          * static <T,C extends Collection<T>> Collector<T,?,C> toCollection(Supplier<C> collectionFactory)
 70          * 產生一個將元素收集到任意集合中的收集器。能夠傳遞一個諸如TreeSet::new的構造器引用。
 71          */
 72         TreeSet<String> noVowelTreeSet = noVowels ()
 73                 .collect(Collectors.toCollection(TreeSet::new));
 74         show ("noVowelTreeSet",noVowelSet);
 75         /**
 76          * static Collector<CharSequence,?,String> joining()
 77          * static Collector<CharSequence,?,String> joining(CharSequence delimiter)
 78          * static Collector<CharSequence,?,String> joining(CharSequence delimiter,
 79          *      CharSequence prefix,CharSequence suffix)
 80          * 產生一個鏈接字符串的收集器。分隔符會置於字符串之間,而第一個字符串以前能夠有前綴,最後一個字符串以後能夠有後綴。
 81          * 若是沒有指定,那麼它們都爲空
 82          */
 83         String result = noVowels ().limit (10).collect (Collectors.joining ());
 84         System.out.println ("Joining:"+result); 
 85         //Joining:Nwydnthvtscnthlpfr
 86         result = noVowels().limit (10).collect (Collectors.joining (","));
 87         System.out.println ("Joining with commas:" + result);
 88         //Joining with commas:Nw,y,dn,t,hv,t,scn,th,lp,fr 
 89         
 90         /**
 91          * static <T> Collector<T,?,IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper) 
 92          * static <T> Collector<T,?,LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper) 
 93          * static <T> Collector<T,?,DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper)
 94          * 產生可以生成(Int|Long|Double)SummaryStatistics 對象的收集,經過它能夠得到將mapper應用於每一個元素後所產生的結果的個數、
 95          * 總和、平均數、最大值、最小值。
 96          */
 97         IntSummaryStatistics summary = noVowels ().collect (
 98                 Collectors.summarizingInt (String::length));
 99         double averageWordLength = summary.getAverage ();
100         double maxWordLength = summary.getMax ();
101         System.out.println ("Average word length:" +averageWordLength);
102         System.out.println ("Max word length:" + maxWordLength);
103         System.out.println ("forEach:");
104 
105         /**
106          * java.util.stream.Stream
107          * void forEach(Consumer<? super T> action)
108          * 在流的每一個元素上調用action。這是一種終結操做。
109          */
110         noVowels ().limit (10).forEach (System.out::println);
111     }
112 }

9、收集到映射表中、羣組和分區

  1 import java.util.*;
  2 import java.util.function.Function; 3 import java.util.stream.Collectors; 4 import java.util.stream.Stream; 5 6 /** 7 * Created by kong on 24/11/2017. 8 */ 9 10 public class CollectingIntoMaps { 11 public static class Person{ 12 private int id; 13 private String name; 14 15 public Person(int id, String name) { 16 this.id = id; 17 this.name = name; 18  } 19 20 public int getId() { 21 return id; 22  } 23 24 public String getName() { 25 return name; 26  } 27 28  @Override 29 public String toString() { 30 return "Person{" + 31 "id=" + id + 32 ", name='" + name + '\'' + 33 '}'; 34  } 35  } 36 public static Stream<Person> people(){ 37 return Stream.of (new Person (1001,"Peter"), 38 new Person (1002,"Paul"), new Person (1003,"Mary")); 39  } 40 41 public static void main(String[] args) { 42 43 44 /** 45 * 46 * static <T, K, U> Collector<T, ?, Map<K,U>> 47 * toMap(Function<? super T, ? extends K> keyMapper, 48 * Function<? super T, ? extends U> valueMapper,BinaryOperator<U> mergeFunction) 49 * 50 * static <T,K,U,M extends ConcurrentMap<K,U>> Collector<T,?,M> 51 * toConcurrentMap(Function<? super T,? extends K> keyMapper, 52 * Function<? super T,? extends U> valueMapper,BinaryOperator<U> 53 * mergeFunction,Supplier<M> mapSupplier) 54 * 55 * 產生一個收集器,它會產生一個映射表或併發映射表。ketMapper和valueMapper函數會應用於每一個收集到的元素上, 56 * 從而在所產生的映射表中生成一個鍵/值項。默認狀況下,當兩個元素產生相同的鍵時,會拋出一個IllegalStateException異常。 57 * 你能夠提供一個mergeFunction來合併具備相同鍵的值。默認狀況下,其結果是一個HashMap或ConcurrentHashMap。 58 * 你能夠提供一個mapSupplier,它會產生所指望的映射表實例。 59 */ 60 Map<Integer,String> idToName = people ().collect ( 61  Collectors.toMap (Person::getId,Person::getName)); 62 System.out.println ("idToName:" + idToName); 63 //idToName:{1001=Peter, 1002=Paul, 1003=Mary} 64 65 66 Map<Integer,Person> idToPerson = people ().collect ( 67  Collectors.toMap (Person::getId, Function.identity ())); 68 System.out.println ("idToPerson:" + idToPerson.getClass ().getName () + idToPerson); 69 /*idToPerson:java.util.HashMap{1001=Person{id=1001, name='Peter'}, 70 1002=Person{id=1002, name='Paul'}, 1003=Person{id=1003, name='Mary'}}*/ 71 72 idToPerson = people ().collect ( 73 Collectors.toMap (Person::getId,Function.identity (),(existingValue,newValue) -> { 74 throw new IllegalStateException (); 75 }, TreeMap::new)); 76 System.out.println ("idToPerson:" +idToPerson.getClass ().getName () + idToPerson); 77 /*idToPerson:java.util.TreeMap{1001=Person{id=1001, name='Peter'}, 78 1002=Person{id=1002, name='Paul'}, 1003=Person{id=1003, name='Mary'}}*/ 79 80 Stream<Locale> locales = Stream.of (Locale.getAvailableLocales ()); 81 Map<String,String> languageNames = locales.collect (Collectors.toMap ( 82 Locale::getDisplayLanguage,l -> l.getDisplayLanguage (l), 83 (existingValue,newValue) -> existingValue)); 84 System.out.println ("languageNames:" + languageNames); 85 /*languageNames:{=, Nyankole=Runyankore, Ewondo=ewondo, Lingala=lingála, 86 Vunjo=Kyivunjo, Norwegian Nynorsk=nynorsk,......}*/ 87 88 locales = Stream.of (Locale.getAvailableLocales ()); 89 Map<String,Set<String>> countryLanguageSets = locales.collect ( 90  Collectors.toMap ( 91 Locale::getDisplayCountry,l -> Collections.singleton (l.getDisplayLanguage ()), 92 (a,b) -> { //union of a and b 93 Set<String> union = new HashSet<> (a); 94  union.addAll (b); 95 return union; 96  })); 97 System.out.println ("countryLanguageSets:" + countryLanguageSets); 98 /*countryLanguageSets:{=[, Nyankole, Ewondo, Lingala,..., Punjabi], 99 Papua New Guinea=[English],...,Greenland=[Danish, Kalaallisut]}*/ 100 101 /** 102 *static <T,K> Collector<T,?,Map<K,List<T>>> 103 * groupingBy(Function<? super T,? extends K> classifier) 104 *static <T,K> Collector<T,?,ConcurrentMap<K,List<T>>> 105 * groupingByConcurrent(Function<? super T,? extends K> classifier) 106 * 生成一個收集器,該收集器生成一個map或併發映射, 107 * 其鍵是將classifier應用於全部收集的元素的結果,其值是具備相同鍵的元素構成的一個個列表。 108 * 109 * static <T> Collector<T,?,Map<Boolean,List<T>>> 110 * partitioningBy(Predicate<? super T> predicate) 111 * 生成一個收集器,它生成一個映射表,其鍵是true/false,其值是實現/不知足斷言的元素構成的列表。 112 */ 113 locales = Stream.of (Locale.getAvailableLocales ()); 114 //將具備相同特性的值羣聚成組是很是見的,而且groupingBy方法直接就支持它。 115 Map<String,List<Locale>> countryToLocales = locales.collect ( 116  Collectors.groupingBy (Locale::getCountry)); 117 List<Locale> swissLocales = countryToLocales.get ("CH"); 118  System.out.println (swissLocales); 119 //[gsw_CH, de_CH, pt_CH, fr_CH, rm_CH, it_CH, wae_CH, en_CH] 120 121 /* 122 當分類器函數是一個斷言函數(即返回一個布爾值的函數)時,流元素被劃分爲兩個列表: 123 函數返回true的元素和其餘運算。在這種狀況下,使用分區而不是經過分組是更有效的。 124 例如,咱們將全部地區劃分爲使用英語和其餘語言的人兩類: 125 */ 126 locales = Stream.of (Locale.getAvailableLocales ()); 127 Map<Boolean,List<Locale>> englishAndOtherLocales = locales.collect ( 128 Collectors.partitioningBy (l -> l.getLanguage ().equals ("en"))); 129 List<Locale> englishLocales = englishAndOtherLocales.get (true); 130  System.out.println (englishLocales); 131 //[en_NU, en_MS, en_GG, en_JM, en_ZM, ...,en_VG, en_TC, en_IN] 132  } 133 }

10、下游收集器

  groupingBy方法生成一個映射表,它的每一個值都是一個列表。若是您想以某種方式處理這些列表,就須要提供一個「下游收集器」。例如,若是你想要得到集而不是而不是列表,那麼可使用前一節中看到的Collector.toSet收集器:

Map<String,Set<Locale>> countryToLocaleSet = locales.collect(

  grounpingBy(Locale::getCountry,toSet()));

 

  1 import java.io.IOException;
  2 import java.nio.file.Files; 3 import java.nio.file.Paths; 4 import java.util.*; 5 import java.util.stream.Stream; 6 7 import static java.util.stream.Collectors.*; 8 9 public class DownstreamCollectors { 10 public static class City{ 11 private String name; 12 private String state; 13 private int population; 14 15 public City(String name, String state, int population) { 16 this.name = name; 17 this.state = state; 18 this.population = population; 19  } 20 21 public String getName() { 22 return name; 23  } 24 25 public String getState() { 26 return state; 27  } 28 29 public int getPopulation() { 30 return population; 31  } 32  } 33 34 public static Stream<City> readCities(String filename) throws IOException { 35 return Files.lines (Paths.get (filename)).map (l -> l.split (",")) 36 .map (a -> new City (a[0],a[1],Integer.parseInt (a[2]))); 37  } 38 39 public static void main(String[] args) throws IOException{ 40 Stream<Locale> locales = Stream.of (Locale.getAvailableLocales ()); 41 42 locales = Stream.of (Locale.getAvailableLocales ()); 43 Map<String,Set<Locale>> countryToLocaleSet = locales.collect (groupingBy( 44  Locale::getCountry,toSet ())); 45 System.out.println ("countryToLocaleSet: " + countryToLocaleSet); 46 //countryToLocaleSet: {=[, nn, bg, ...,lrc, ses, ce],PR=[es_PR, en_PR], PS=[ar_PS], PT=[pt_PT], PW=[en_PW],...} 47 48 /** 49 * static <T> Collector<T, ?, Long> counting() 50 * 產生一個能夠對收集到的元素進行計數的收集器。 51 */ 52 locales = Stream.of (Locale.getAvailableLocales ()); 53 Map<String,Long> countryToLocaleCounts = locales.collect (groupingBy ( 54  Locale::getCountry,counting ())); 55 System.out.println ("countryToLocaleCounts: " +countryToLocaleCounts); 56 //countryToLocaleCounts: {=214, PR=2, PS=1, PT=1, PW=1, PY=1, QA=1, AD=1, ...,AW=1} 57 58 /** 59 * static <T> Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper) 60 * static <T> Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper) 61 * static <T> Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper) 62 * 產生一個收集器,對mapper應用到收集到的元素上以後產生的值進行計算總和 63 */ 64 Stream<City> cities = readCities ("cities.txt"); 65 Map<String, IntSummaryStatistics> stateToCityPopulation = cities.collect (groupingBy ( 66  City::getState,summarizingInt (City::getPopulation))); 67 System.out.println ("stateToCityPopulation: " + stateToCityPopulation); 68 69 cities = readCities ("cities.txt"); 70 Map<String,IntSummaryStatistics> stateToCityPopulationSummary = cities 71  .collect (groupingBy ( 72  City::getState, 73  summarizingInt (City::getPopulation))); 74 System.out.println ("stateToCityPopulationSummary: " + stateToCityPopulationSummary); 75 76 77 /** 78 * static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator) 79 * static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator) 80 * 產生一個收集器,使用comparator指定的排序方法,計數收集到的元素中的最大值和最小值。 81 */ 82 cities = readCities ("cities.txt"); 83 Map<String,Optional<String>> stateToLongestCityName = cities 84  .collect (groupingBy ( 85  City::getState, 86  mapping (City::getName, 87  maxBy (Comparator.comparing (String::length))))); 88 System.out.println ("stateToLongestCityName: " + stateToLongestCityName); 89 90 /** 91 * static <T, U, A, R>Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper, 92 * Collector<? super U, A, R> downstream) 93 * 產生一個收集器,它產生一個映射表,其鍵是將mapper應用到收集到的數據上而產生的, 94 * 其值是使用downstream收集器收集到的具備相同鍵的元素 95 */ 96 locales = Stream.of (Locale.getAvailableLocales ()); 97 Map<String,Set<String>> countryToLanguages = locales.collect (groupingBy ( 98  Locale::getDisplayCountry,mapping (Locale::getDisplayLanguage,toSet ()))); 99 System.out.println ("countryToLanguages: " + countryToLanguages); 100 /*countryToLanguages: {=[, Nyankole, Ewondo, Lingala,... ,Bemba, Hungarian, Zarma, Punjabi], 101 Papua New Guinea=[English], Cambodia=[Khmer], Paraguay=[Spanish], Kazakhstan=[Kazakh, Russian],...}*/ 102 103 104 cities = readCities ("cities.txt"); 105 Map<String,String> stateToCityNames = cities 106  .collect (groupingBy ( 107  City::getState, 108 reducing ("",City::getName,(s,t) -> s.length () == 0 ? t : s))); 109 110 cities = readCities ("cities.txt"); 111 stateToCityNames = cities.collect (groupingBy (City::getState, 112 mapping (City::getName, joining(",")))); 113 System.out.println ("stateToCityNames: " + stateToCityNames); 114  } 115 }

11、約簡操做

   reduce方法是一種用於從流中計算某值的通用機制,其最簡單的形式接受一個二元函數,並從前兩個元素開始持續應用它。若是該函數是求和函數,那麼很容易解釋這種機制:

List<I nteger> values = ...;

Optional<Integer> sum = values.stream().reduce((x, y) - > x + y);  

//reduce((x, y) - > x + y)能夠寫成reduce(Integer::sum);

  在上面的狀況中,reduce方法會計算v0+v1+v2+...,其中vi是流中的元素。若是流爲空,那該方法會返回一個Optional,由於沒有任何有效的結果。 

12、基本類型流

  咱們都是將整數收集到Stream<Integer>中,將每一個整數封裝到一個包裝器對象中是很低效的。對於其餘的基本類型來講,一樣的狀況也適用於double、float、long、short、char、byte和boolean。流庫擁有特殊類型的IntStream、LongStream和DoubleStream,它們直接存儲基本類型值,而不使用包裝器。若是您想要存儲short、char、byte和boolean,使用一個IntStream,而對於float,則使用DoubleStream。使用方式與Stream類似。

 1 import java.io.IOException;
 2 import java.nio.charset.StandardCharsets; 3 import java.nio.file.Files; 4 import java.nio.file.Path; 5 import java.nio.file.Paths; 6 import java.util.stream.Collectors; 7 import java.util.stream.IntStream; 8 import java.util.stream.Stream; 9 10 public class PrimitiveTypeStreams { 11 public static void show(String title, IntStream stream){ 12 final int SIZE = 10; 13 int[] firstElements = stream.limit (SIZE+1).toArray (); 14 System.out.print (title+":"); 15 for (int i = 0; i < firstElements.length; i++){ 16 if (i > 0) System.out.print (","); 17 if (i < SIZE) System.out.print (firstElements[i]); 18 else System.out.print ("..."); 19  } 20  System.out.println (); 21  } 22 23 public static void main(String[] args) throws IOException { 24 /** 25 * java.util.stream.IntStream 26 * static IntStream range(int startInclusive, int endExclusive) 27 * static IntStream rangeClosed(int startInclusive, int endInclusive) 28 * 產生一個由給定範圍內容的整數構成的IntStream 29 * 30 * static IntStream of(int...values) 31 * 產生一個由當前流中的元素構成的數組 32 * int sum() 33 * OptionalDouble average() 34 * OptionalInt max() 35 * OptionalInt min() 36 * IntSummaryStatistics summaryStatistics() 37 * 產生當前流中元素的總和、平均值、最大值和最小值,或者從中能夠得到這些結果全部四種值的對象。 38 * 39 * Stream<Integer> boxed() 40 * 產生用於當前流中的元素的包裝器對象流 41 */ 42 IntStream is1 = IntStream.generate (() -> (int)(Math.random()*100)); 43 show ("is1",is1); 44 //is1:51,94,77,28,68,43,98,34,21,95,... 45 IntStream is2 = IntStream.range (5,10); 46 show ("is2",is2); 47 //is2:5,6,7,8,9 48 IntStream is3 = IntStream.rangeClosed (5,10); 49 show ("is3",is3); 50 //is3:5,6,7,8,9,10 51 52 Path path = Paths.get ("alice.txt"); 53 String contents = new String (Files.readAllBytes (path), StandardCharsets.UTF_8); 54 55 Stream<String> words = Stream.of (contents.split ("\\PL+")); 56 IntStream is4 = words.mapToInt (String::length); 57 show ("is4",is4); 58 //is4:3,3,3,1,4,2,4,3,4,3,... 59 60 String sentence ="\uD835\uDD46 is the set of octonions."; 61  System.out.println (sentence); 62 //𝕆 is the set of octonions. 63 IntStream codes = sentence.codePoints (); 64 System.out.println (codes.mapToObj (c -> String.format ("%X",c)).collect ( 65  Collectors.joining ())); 66 //1D5462069732074686520736574206F66206F63746F6E696F6E732E 67 68 Stream<Integer> integers = IntStream.range (0,100).boxed (); 69 IntStream is5 = integers.mapToInt (Integer::intValue); 70 show ("is5",is5); 71 //is5:0,1,2,3,4,5,6,7,8,9,... 72  } 73 }

十3、並行流

  流使得並行化批量操做變得很容易。這個過程基本上是自動的,可是您須要遵循一些規則。首先,你必須有一個並行流。你能夠用Collection.parallelStream()方法從任何集合中獲取一個並行流:

Stream<String> parallelWords = words.parallelStream();   

  此外,parallel方法能夠將任何順序流轉換爲並行流。

Stream<String> parallelWords = Stream.of(wordArray).parallel();   

  只要在終結方法執行時流處於並行模式,全部中間流操做都將被並行化。

  默認狀況下,來自有序集合(數組和列表)、範圍、生成器和迭代器產生的流,或者經過調用Stream.sorted產生的流,都是有序的。它們結果是按照原始元素的順序積累的,而且是徹底可預測的。若是您兩次運行相同的操做,您將獲得徹底相同的結果。

  排序並不排斥高效的並行化處理。例如,當計算Stream.map(fun)時,能夠將流劃分爲n段,每一個段都是併發處理的。而後,結果會按順序從新組合。

  當排序需求被刪除時,一些操做能夠更有效地並行化。經過在流中調用unordered方法,代表對排序不感興趣。

  爲了使並行流運行良好,須要知足許多條件:  

  • 數據應該在內存中。必須等待數據到達是很是低效的。
  • 流應該能夠被高效的分紅若干個子部分。由數組或平衡的二叉樹支撐的流能夠很好地工做,可是Stream.iterate返回的結果不行。
  • 流操做應該作大量的工做。若是總工做負載不是很大,那麼爲創建並行計算付出的代價是沒有意義的。
  • 流操做不該該阻塞。

  換句話說,不要把全部的流都變成並行的流。只有在對已經存在於內存中的數據進行大量的持續計算工做時,才使用並行流。

 1 import java.io.IOException;
 2 import java.nio.charset.StandardCharsets; 3 import java.nio.file.Files; 4 import java.nio.file.Paths; 5 import java.util.Arrays; 6 import java.util.List; 7 import java.util.Map; 8 import static java.util.stream.Collectors.counting; 9 import static java.util.stream.Collectors.groupingBy; 10 import static java.util.stream.Collectors.groupingByConcurrent; 11 12 public class ParallelStreams { 13 public static void main(String[] args) throws IOException{ 14 15 /** 16 * java.util.stream.BaseStream<T,S extends BaseStream<T,S>> 17 * S parallel() 18 * 產生一個與當前流中元素相同的並行流 19 * S unordered() 20 * 產生一個與當前流中元素相同的無序流 21 * 22 * java.util.Collection<E> 23 * Stream<E> parallelStream() 24 * 用當前集合中的元素產生一個並行流。 25 */ 26 27 String contents = new String (Files.readAllBytes ( 28 Paths.get ("alice.txt")), StandardCharsets.UTF_8); 29 List<String> wordList = Arrays.asList (contents.split ("\\PL+")); 30 31 //Very bad code ahead 32 int[] shotWords = new int[10]; 33 wordList.parallelStream ().forEach ( s -> { 34 if (s.length () < 10) shotWords[s.length ()]++; 35  }); 36  System.out.println (Arrays.toString (shotWords)); 37 //[0, 2, 13, 17, 11, 6, 6, 4, 7, 1] 38 39 //Try again--the result will likely be different(and also wrong) 40 Arrays.fill (shotWords,0); 41 wordList.parallelStream ().forEach (s -> { 42 if (s.length () < 10) shotWords[s.length ()]++; 43  }); 44  System.out.println (Arrays.toString (shotWords)); 45 //[0, 2, 13, 17, 11, 6, 6, 4, 7, 1] 46 47 //Remedy: Group and count 48 Map<Integer,Long> shortWordCounts = wordList.parallelStream () 49 .filter (s -> s.length () < 10 ) 50  .collect (groupingBy(String::length,counting ())); 51 52  System.out.println (shortWordCounts); 53 //{1=2, 2=13, 3=17, 4=11, 5=6, 6=6, 7=4, 8=7, 9=1} 54 55 //DownStream order not deterministic 56 Map<Integer,List<String>> result = wordList.parallelStream ().collect ( 57  groupingByConcurrent (String::length)); 58 59 System.out.println (result.get (14)); 60 //null 61 62 result = wordList.parallelStream ().collect ( 63  groupingByConcurrent (String::length)); 64 65 System.out.println (result.get (14)); 66 //null 67 68 Map<Integer,Long> wordCounts = wordList.parallelStream ().collect ( 69  groupingByConcurrent (String::length,counting ())); 70 71  System.out.println (wordCounts); 72 //{1=2, 2=13, 3=17, 4=11, 5=6, 6=6, 7=4, 8=7, 9=1, 10=4, 12=2} 73 74  } 75 }
相關文章
相關標籤/搜索