前言:流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图。如:

  1. //使用foreach迭代
  2. long count = 0;
  3. for (String w : words) {
  4. if (w.length () > 6) count++;
  5. }
  6.  
  7. //使用流
  8. long count = words.stream ()
  9. .filter (w -> w.length () > 6 )
  10. .count ();

流的版本更易于阅读,流遵循了“做什么而非怎么做”的原则。

一、什么是流

  Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

  Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

  而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

  Stream 的另外一大特点是,数据源本身可以是无限的。

  流表面上和集合很类似,都可以让我们转换和获取数据,但它们之间存在显著的差异:

1、流并不存储其元素。这些元素可能存储在底层的集合中,或者按需生成的。

2、流的操作不会修改其数据源,例如,filter方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含过滤掉的元素。

3、流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。

二、流的构成

  如Demo01所示,这个工作流是操作流时的典型流程,我们建立了一个包含三个阶段的操作管道:

1、创建一个流;

2、指定初始流转换为其他流的中间操作,可能包含多个步骤;

3、应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作,从此之后这个流就再也不能用了。

  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. }

三、流的操作

  流的操作类型分为两种:

  • Intermediate(转换操作):一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
  • Terminal(终止操作):一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。

四、流的创建

  • 通过集合类中的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. }

五、处理流的转换

   流的转换会产生一个新的流,它的元素派生自另外一个流中的元素。

  1、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应用于当前流中所有元素所产生的结果连接到一起而获得的。(这里的每个结果都是一个流。)

  2、抽取子流和连接流

  ①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的元素。

  3、其他的转换流

  ①Stream<T> distinct()

  产生一个流,包含当前流中所有不同的元素。

  1. 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。如:

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

  当实际访问一个元素时,就会打印出一条消息。通过这种方式,你可以验证iterate返回的无限流是被惰性处理的。

  对应调试,你可以让peek调用一个你设置了断点的方法。

六、简单约简

  约简是一种终结操作(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,这些操作都是终结操作。

七、Optional类型

  Optional<T> 对象是一种包装器对象,要么包装了类型T的对象,要么没有包装任何对象。

  1、如何使用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。

  2、不适合使用Optional值的方式

T get()

  产生这个Option的值,或者在该Optional为空时,抛出一个NoSuchElementException对象。

boolean isPresent()

  如果该Optional不为空,则返回true。

  3、创建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。 

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

  ofNullable(obj)方法被用来作为可能出现的null值和可选值之间的桥梁。

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

八、收集结果

  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. }

九、收集到映射表中、群组和分区

  1. 1 import java.util.*;
  2. 2 import java.util.function.Function;
  3. 3 import java.util.stream.Collectors;
  4. 4 import java.util.stream.Stream;
  5. 5
  6. 6 /**
  7. 7 * Created by kong on 24/11/2017.
  8. 8 */
  9. 9
  10. 10 public class CollectingIntoMaps {
  11. 11 public static class Person{
  12. 12 private int id;
  13. 13 private String name;
  14. 14
  15. 15 public Person(int id, String name) {
  16. 16 this.id = id;
  17. 17 this.name = name;
  18. 18 }
  19. 19
  20. 20 public int getId() {
  21. 21 return id;
  22. 22 }
  23. 23
  24. 24 public String getName() {
  25. 25 return name;
  26. 26 }
  27. 27
  28. 28 @Override
  29. 29 public String toString() {
  30. 30 return "Person{" +
  31. 31 "id=" + id +
  32. 32 ", name='" + name + '\'' +
  33. 33 '}';
  34. 34 }
  35. 35 }
  36. 36 public static Stream<Person> people(){
  37. 37 return Stream.of (new Person (1001,"Peter"),
  38. 38 new Person (1002,"Paul"), new Person (1003,"Mary"));
  39. 39 }
  40. 40
  41. 41 public static void main(String[] args) {
  42. 42
  43. 43
  44. 44 /**
  45. 45 *
  46. 46 * static <T, K, U> Collector<T, ?, Map<K,U>>
  47. 47 * toMap(Function<? super T, ? extends K> keyMapper,
  48. 48 * Function<? super T, ? extends U> valueMapper,BinaryOperator<U> mergeFunction)
  49. 49 *
  50. 50 * static <T,K,U,M extends ConcurrentMap<K,U>> Collector<T,?,M>
  51. 51 * toConcurrentMap(Function<? super T,? extends K> keyMapper,
  52. 52 * Function<? super T,? extends U> valueMapper,BinaryOperator<U>
  53. 53 * mergeFunction,Supplier<M> mapSupplier)
  54. 54 *
  55. 55 * 产生一个收集器,它会产生一个映射表或并发映射表。ketMapper和valueMapper函数会应用于每个收集到的元素上,
  56. 56 * 从而在所产生的映射表中生成一个键/值项。默认情况下,当两个元素产生相同的键时,会抛出一个IllegalStateException异常。
  57. 57 * 你可以提供一个mergeFunction来合并具有相同键的值。默认情况下,其结果是一个HashMap或ConcurrentHashMap。
  58. 58 * 你可以提供一个mapSupplier,它会产生所期望的映射表实例。
  59. 59 */
  60. 60 Map<Integer,String> idToName = people ().collect (
  61. 61 Collectors.toMap (Person::getId,Person::getName));
  62. 62 System.out.println ("idToName:" + idToName);
  63. 63 //idToName:{1001=Peter, 1002=Paul, 1003=Mary}
  64. 64
  65. 65
  66. 66 Map<Integer,Person> idToPerson = people ().collect (
  67. 67 Collectors.toMap (Person::getId, Function.identity ()));
  68. 68 System.out.println ("idToPerson:" + idToPerson.getClass ().getName () + idToPerson);
  69. 69 /*idToPerson:java.util.HashMap{1001=Person{id=1001, name='Peter'},
  70. 70 1002=Person{id=1002, name='Paul'}, 1003=Person{id=1003, name='Mary'}}*/
  71. 71
  72. 72 idToPerson = people ().collect (
  73. 73 Collectors.toMap (Person::getId,Function.identity (),(existingValue,newValue) -> {
  74. 74 throw new IllegalStateException ();
  75. 75 }, TreeMap::new));
  76. 76 System.out.println ("idToPerson:" +idToPerson.getClass ().getName () + idToPerson);
  77. 77 /*idToPerson:java.util.TreeMap{1001=Person{id=1001, name='Peter'},
  78. 78 1002=Person{id=1002, name='Paul'}, 1003=Person{id=1003, name='Mary'}}*/
  79. 79
  80. 80 Stream<Locale> locales = Stream.of (Locale.getAvailableLocales ());
  81. 81 Map<String,String> languageNames = locales.collect (Collectors.toMap (
  82. 82 Locale::getDisplayLanguage,l -> l.getDisplayLanguage (l),
  83. 83 (existingValue,newValue) -> existingValue));
  84. 84 System.out.println ("languageNames:" + languageNames);
  85. 85 /*languageNames:{=, Nyankole=Runyankore, Ewondo=ewondo, Lingala=lingála,
  86. 86 Vunjo=Kyivunjo, Norwegian Nynorsk=nynorsk,......}*/
  87. 87
  88. 88 locales = Stream.of (Locale.getAvailableLocales ());
  89. 89 Map<String,Set<String>> countryLanguageSets = locales.collect (
  90. 90 Collectors.toMap (
  91. 91 Locale::getDisplayCountry,l -> Collections.singleton (l.getDisplayLanguage ()),
  92. 92 (a,b) -> { //union of a and b
  93. 93 Set<String> union = new HashSet<> (a);
  94. 94 union.addAll (b);
  95. 95 return union;
  96. 96 }));
  97. 97 System.out.println ("countryLanguageSets:" + countryLanguageSets);
  98. 98 /*countryLanguageSets:{=[, Nyankole, Ewondo, Lingala,..., Punjabi],
  99. 99 Papua New Guinea=[English],...,Greenland=[Danish, Kalaallisut]}*/
  100. 100
  101. 101 /**
  102. 102 *static <T,K> Collector<T,?,Map<K,List<T>>>
  103. 103 * groupingBy(Function<? super T,? extends K> classifier)
  104. 104 *static <T,K> Collector<T,?,ConcurrentMap<K,List<T>>>
  105. 105 * groupingByConcurrent(Function<? super T,? extends K> classifier)
  106. 106 * 生成一个收集器,该收集器生成一个map或并发映射,
  107. 107 * 其键是将classifier应用于所有收集的元素的结果,其值是具有相同键的元素构成的一个个列表。
  108. 108 *
  109. 109 * static <T> Collector<T,?,Map<Boolean,List<T>>>
  110. 110 * partitioningBy(Predicate<? super T> predicate)
  111. 111 * 生成一个收集器,它生成一个映射表,其键是true/false,其值是实现/不满足断言的元素构成的列表。
  112. 112 */
  113. 113 locales = Stream.of (Locale.getAvailableLocales ());
  114. 114 //将具有相同特性的值群聚成组是非常见的,并且groupingBy方法直接就支持它。
  115. 115 Map<String,List<Locale>> countryToLocales = locales.collect (
  116. 116 Collectors.groupingBy (Locale::getCountry));
  117. 117 List<Locale> swissLocales = countryToLocales.get ("CH");
  118. 118 System.out.println (swissLocales);
  119. 119 //[gsw_CH, de_CH, pt_CH, fr_CH, rm_CH, it_CH, wae_CH, en_CH]
  120. 120
  121. 121 /*
  122. 122 当分类器函数是一个断言函数(即返回一个布尔值的函数)时,流元素被划分为两个列表:
  123. 123 函数返回true的元素和其他运算。在这种情况下,使用分区而不是通过分组是更有效的。
  124. 124 例如,我们将所有地区划分为使用英语和其他语言的人两类:
  125. 125 */
  126. 126 locales = Stream.of (Locale.getAvailableLocales ());
  127. 127 Map<Boolean,List<Locale>> englishAndOtherLocales = locales.collect (
  128. 128 Collectors.partitioningBy (l -> l.getLanguage ().equals ("en")));
  129. 129 List<Locale> englishLocales = englishAndOtherLocales.get (true);
  130. 130 System.out.println (englishLocales);
  131. 131 //[en_NU, en_MS, en_GG, en_JM, en_ZM, ...,en_VG, en_TC, en_IN]
  132. 132 }
  133. 133 }

十、下游收集器

  groupingBy方法生成一个映射表,它的每个值都是一个列表。如果您想以某种方式处理这些列表,就需要提供一个“下游收集器”。例如,如果你想要获得集而不是而不是列表,那么可以使用前一节中看到的Collector.toSet收集器:

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

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

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

十一、约简操作

   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,因为没有任何有效的结果。

十二、基本类型流

  我们都是将整数收集到Stream<Integer>中,将每个整数封装到一个包装器对象中是很低效的。对于其他的基本类型来说,同样的情况也适用于double、float、long、short、char、byte和boolean。流库拥有特殊类型的IntStream、LongStream和DoubleStream,它们直接存储基本类型值,而不使用包装器。如果您想要存储short、char、byte和boolean,使用一个IntStream,而对于float,则使用DoubleStream。使用方式与Stream相似。

  1. 1 import java.io.IOException;
  2. 2 import java.nio.charset.StandardCharsets;
  3. 3 import java.nio.file.Files;
  4. 4 import java.nio.file.Path;
  5. 5 import java.nio.file.Paths;
  6. 6 import java.util.stream.Collectors;
  7. 7 import java.util.stream.IntStream;
  8. 8 import java.util.stream.Stream;
  9. 9
  10. 10 public class PrimitiveTypeStreams {
  11. 11 public static void show(String title, IntStream stream){
  12. 12 final int SIZE = 10;
  13. 13 int[] firstElements = stream.limit (SIZE+1).toArray ();
  14. 14 System.out.print (title+":");
  15. 15 for (int i = 0; i < firstElements.length; i++){
  16. 16 if (i > 0) System.out.print (",");
  17. 17 if (i < SIZE) System.out.print (firstElements[i]);
  18. 18 else System.out.print ("...");
  19. 19 }
  20. 20 System.out.println ();
  21. 21 }
  22. 22
  23. 23 public static void main(String[] args) throws IOException {
  24. 24 /**
  25. 25 * java.util.stream.IntStream
  26. 26 * static IntStream range(int startInclusive, int endExclusive)
  27. 27 * static IntStream rangeClosed(int startInclusive, int endInclusive)
  28. 28 * 产生一个由给定范围内容的整数构成的IntStream
  29. 29 *
  30. 30 * static IntStream of(int...values)
  31. 31 * 产生一个由当前流中的元素构成的数组
  32. 32 * int sum()
  33. 33 * OptionalDouble average()
  34. 34 * OptionalInt max()
  35. 35 * OptionalInt min()
  36. 36 * IntSummaryStatistics summaryStatistics()
  37. 37 * 产生当前流中元素的总和、平均值、最大值和最小值,或者从中可以获得这些结果所有四种值的对象。
  38. 38 *
  39. 39 * Stream<Integer> boxed()
  40. 40 * 产生用于当前流中的元素的包装器对象流
  41. 41 */
  42. 42 IntStream is1 = IntStream.generate (() -> (int)(Math.random()*100));
  43. 43 show ("is1",is1);
  44. 44 //is1:51,94,77,28,68,43,98,34,21,95,...
  45. 45 IntStream is2 = IntStream.range (5,10);
  46. 46 show ("is2",is2);
  47. 47 //is2:5,6,7,8,9
  48. 48 IntStream is3 = IntStream.rangeClosed (5,10);
  49. 49 show ("is3",is3);
  50. 50 //is3:5,6,7,8,9,10
  51. 51
  52. 52 Path path = Paths.get ("alice.txt");
  53. 53 String contents = new String (Files.readAllBytes (path), StandardCharsets.UTF_8);
  54. 54
  55. 55 Stream<String> words = Stream.of (contents.split ("\\PL+"));
  56. 56 IntStream is4 = words.mapToInt (String::length);
  57. 57 show ("is4",is4);
  58. 58 //is4:3,3,3,1,4,2,4,3,4,3,...
  59. 59
  60. 60 String sentence ="\uD835\uDD46 is the set of octonions.";
  61. 61 System.out.println (sentence);
  62. 62 //
  63. Java SE 8 的流库学习笔记的更多相关文章

      1. 20145213Java程序设计》第八周学习笔记
      1. 20145213<Java程序设计>第八周学习笔记 教材学习内容总结 "桃花春欲尽,谷雨夜来收"谷雨节气的到来意味着寒潮天气的基本结束,气温回升加快.刚出冬的我对于这种 ...

      1. numpy, matplotlib库学习笔记
      1. Numpy库学习笔记: 1.array()   创建数组或者转化数组 例如,把列表转化为数组 >>>Np.array([1,2,3,4,5]) Array([1,2,3,4,5]) ...

      1. muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor
      1. 目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...

      1. muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制
      1. 目录 muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制 eventfd的使用 eventfd系统函数 使用示例 EventLoopeventfd的封装 工作时序 runInLoo ...

      1. muduo网络库学习笔记(三)TimerQueue定时器队列
      1. 目录 muduo网络库学习笔记(三)TimerQueue定时器队列 Linux中的时间函数 timerfd简单使用介绍 timerfd示例 muduo中对timerfd的封装 TimerQueue的结 ...

      1. Java架构师-十项全能学习笔记(1
      1. Java架构师-十项全能学习笔记(1) @Configuration @EnableStateMachine public class OrderStateMachineConfig extends ...

      1. C++STL标准库学习笔记(三)multiset
      1. C++STL标准库学习笔记(三)multiset STL中的平衡二叉树数据结构 前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标 ...

      1. Java并发之底层实现原理学习笔记
      1. 本篇博文将介绍java并发底层的实现原理,我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为 ...

      1. Java架构师之路 Spring学习笔记(一) Spring介绍
      1. 前言 这是一篇原创的Spring学习笔记.主要记录我学习Spring4.0的过程.本人有四年的Java Web开发经验,最近在面试中遇到面试官总会问一些简单但我不会的Java问题,让我觉得有必要重新审 ...

    1.  
    2. 随机推荐

        1. npm start时报错 npm ERRWindows_NT 6.1.7601
        1. 练习webpack 输入 npm start就报这样的错.百度了一圈,都没有找到答案.于是,我开始看错误信息......................................../手动黑 ...

        1. 浅谈Android选项卡(三)
        1. 上一节介绍了TabActivity的简单用法,但是现在的Api中已经不建议使用了,建议使用Fragment来替代以上的功能,下面介绍下使用FragmentViewPager的结合使用. http:/ ...

        1. 我把阿里云centos gcc4.4.7升级到4.8.2的经历
        1. 我有试着去手动编译安装gcc,可是make的速度实在太慢,最后还直接失败了. 最后在csdn找到了个博客,说是使用yum来安装,网址为: http://blog.csdn.net/ppdouble/a ...

        1. 0weka学习与使用
        1. 转载自:https://blog.csdn.net/u011067360/article/details/20844443 数据挖掘开源软件:WEKA基础教程 本文档部分来自于网络,随着自己的深入学习 ...

        1. ubuntu14 安装tftp服务器
        1. 安装 sudo apt-get install tftp-hpa tftpd-hpa 配置 sudo gedit /etc/default/tftpd-hpa 打开tftpd-hpa修改里面的配置: ...

        1. 集合之五:Set接口(答案)
        1. package com.shsxt.homework; import java.util.ArrayList; import java.util.Collection; import java.uti ...

        1. Java Web入门学习(二) Eclipse的配置
        1. Java Web学习(二) Eclipse的配置 一.下载Eclipse 1.进入Eclipse官网,进行下载 上图,下载Eclipse IDE for JaveEE Developers 版本,然后 ...

        1. Linux系统编程:文件I/O编程
        1. 文件I/O操作在Linux编程时是经常会使用到的一个内容,通常可以把比较大的数据写到一个文件中来进行存储或者与其他进程进行数据传输,这样比写到一个全局数组或指针参数来实现还要方便好多. 一.文件常用操 ...

        1. PHP 导入数据库 sql 文件
        1. 使用PHP 可以导入sql来建立数据库.代码如下: <?php $hostname = 'localhost'; $dbname = 'test'; $username = 'root'; $p ...

        1. c++ 网络编程(十一) LINUX 初步制作基于HTTPWEB服务器
        1. 原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/9663028.html HTTP概要 理解Web服务器端: 编写HTTP(超文本传输协议)服务器 ...