前言:流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图。如:
//使用foreach迭代
long count = 0;for(String w : words) {if (w.length () > 6) count++;
}//使用流
long count =words.stream ()
.filter (w-> w.length () > 6)
.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 packagechapter01;2 importjava.io.IOException;3 importjava.nio.charset.StandardCharsets;4 importjava.nio.file.Files;5 importjava.nio.file.Paths;6 importjava.util.Arrays;7 importjava.util.List;8
9 public classDemo01 {10 public static void main(String[] args) throwsIOException {11 String contents = newString (adAllBytes ( (")), StandardCharsets.UTF_8);13 List 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 packagechapter01;2
3 importjava.io.IOException;4 importjava.math.BigInteger;5 importjava.nio.charset.StandardCharsets;6 importjava.nio.file.Files;7 importjava.nio.file.Path;8 importjava.nio.file.Paths;9 importjava.util.Arrays;10 importjava.util.List;11 Pattern;12 importjava.util.stream.Collectors;13 importjava.util.stream.Stream;14
15 public classCreatingStreams {16 public static void show(String title, Streamstream){17 final int SIZE = 10;18 List firstElements =stream19 .limit (SIZE+1)20 .List());21 System.out.print (title + ":");22 for (int i = 0; i < firstElements.size (); i++) {23 if (i > 0) System.out.print (",");24 if (i
34 /**
35 * static Stream values)36 * 产生一个元素为指定值的流37 */
38 Stream words = Stream.of (contents.split ("\PL+"));//以标点符号为分隔符
39 show ("words",words);40 //words:Now,you,don,t,have,to,scan,the,loop,for,...
41 Stream song = Stream.of ("gently","down","the","stream");42 show ("song",song);43 //song:gently,down,the,stream
44
45 /**
46 * static Stream empty()47 * 产生一个不包含任何元素的流48 */
49 Stream silence pty ();50 show ("silence",silence);51 //silence:
52
53 /**
54 * static Stream generate(Supplier s)55 * 产生一个无限流,它的值是通过反复调用函数s而构建的56 */
57 Stream echos = ate (() -> "Echo");58 show ("echos",echos);59 //echos:Echo,Echo,Echo,Echo,Echo,Echo,Echo,Echo,Echo,Echo,...
60
61 Stream randoms ate (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 Stream iterate(T seed, UnaryOperator f)69 * 产生一个无限流,它的元素包含种子、在种子上调用f产生的值、在前一个元素上调用f产生的值,等等70 */
71 Stream 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 Stream stream(T[] array, int startInclusive, int endExclusive)78 * 产生一个流,它的元素是数组中指定范围内的元素构成的。79 */
80 String[] strings = {"A","B","C","D","E"};81 Stream stringStream = Arrays.stream (strings,0,3);82 show ("stringStream",stringStream);83 //stringStream:A,B,C
84
85 /**
86 * Stream spiltAsStream(CharSequence input)87 * 产生一个流,它的元素是输入中由该模式界定的部分88 */
89 Stream wordsAnotherWay = Patternpile ("\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 795 * static Stream lines(Path path)96 * static Stream lines(Path path,Charset cs)97 * 产生一个流,它的元素是指定文件中的行,该文件的字符集为UTF-8,或者为指定的字符集98 */
99 try (Stream 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 filter(Predicate super T> predicate)
产生一个流,它包含当前流中所有满足断言条件的元素。如将一个字符串流转换为了只含长单词的另一个流:
List wordList = ...;
Stream longWords = wordList.stream().filter(w -> w.length() > 12);
② Stream map(Function super T,? extends R> mapper)
产生一个流,它包含将mapper应用于当前流中所有元素所产生的结果。如:将所有单词都转换为小写:
Stream lowercaseWords = words.stream().map(String::toLowerCase);
③ Stream flatMap(Function super T,? extends R> mapper)
产生一个流,它通过将mapper应用于当前流中所有元素所产生的结果连接到一起而获得的。(这里的每个结果都是一个流。)
2、抽取子流和连接流
①Stream limit(long maxSize)
产生一个流,其中包含了当前流中最初的maxSize个元素(如果原来的流更短,那么就会在流结束时结束)。这个方法对于裁剪无限流的尺寸会显得特别有用,如:产生一个包含100个随机数的流
Stream randoms = ate(Math::random).limit(100);
②Stream skip(long n)
产生一个流,它的元素是当前流中除了前n个元素之外的所有元素。
③static Stream concat(Stream extends T> a, Stream extends T> b)
产生一个流,它的元素是a的元素后面跟着b的元素。
3、其他的转换流
①Stream distinct()
产生一个流,包含当前流中所有不同的元素。
Stream uniqueWords
= Stream.of ("merrily","merrily","merrily","gently").distinct ();
//Only one "merrily" is retained
② Stream sorted()
Stream sorted(Comparator super T> comparator)
产生一个流,它的元素是当前流中的所有元素按照顺序排列的。第一方法要求元素实现了Comparable的类的实例。
Stream longestFirst =
words.stream().sorted(Comparatorparing(String::length).reversed());
//长字符排在前
③Stream 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调用一个你设置了断点的方法。
六、简单约简
约简是一种终结操作(terminal operation),它们会将流约简为可以在程序中使用的非流值。
Optional max(Comparator super T> comparator)
Optional min(Comparator super T> comparator)
分别产生这个流的最大元素和最小元素,使用由给定比较器定义的排序规则,如果这流为空,会产生一个空的Optional对象。这些操作都是终结操作。
Optional findFirst()
Optional findAny()
分别产生这个流的第一个元素和任意一个元素,如果这流为空,会产生一个空的Optional对象。这些操作都是终结操作。
boolean anyMatch(Predicate super T> predicate)
boolean allMatch(Predicate super T> predicate)
boolean noneMatch(Predicate super T> predicate)
分别在这个流中任意元素、所有元素和没有任何元素匹配给定端言时返回true,这些操作都是终结操作。
七、Optional类型
Optional 对象是一种包装器对象,要么包装了类型T的对象,要么没有包装任何对象。
1、如何使用Optional值
策略一:在没有任何匹配时,使用某种默认值,可以是空字符串。
T orElse(T other)
如:String result = Else("");//The wrapped string,or "" if none
产生这个Optional的值,或者在该Optional为空时,产生other。
T orElseGet(Supplier extends T> other)
如:String result = ElseGet(() -> Default().getDisplayName());
//The function is only called when needed
产生这个Optional的值,或者在该Optional为空时,产生调用other的结果。
T orElseThrow(Supplier extends X> exceptionSupplier)
如:String result = ElseThrow(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。
Optional map(Function super T,? extends U> mapper)
如:Optional added = optionalValue.map(results::add);
产生将该Optional的值传递给mapper后的结果,只要这个Optional不为空且结果不为null,否则产生一个空Optional。
2、不适合使用Optional值的方式
T get()
产生这个Option的值,或者在该Optional为空时,抛出一个NoSuchElementException对象。
boolean isPresent()
如果该Optional不为空,则返回true。
3、创建Optional
static Optional of(T value)
static Optional ofNullable(T value)
产生一个具有给定值的Optional。如果value为null,那么第一个方法会抛出一个NullPointerException对象,而第二方法会产生一个空Optional。
static Optional empty()
产生一个空Optional。
public static Optionalinverse(Double x){return x == 0 ? pty() : Optional.of(1 /x);
}
ofNullable(obj)方法被用来作为可能出现的null值和可选值之间的桥梁。
4、用flatMap来构建Optional值的函数
Optional flatMap(Function super T,Optional> mapper)
产生将mapper应用于当前的Optional值所产生的结果,或者在当前Optional为空时,返回一个空Optional。
1 importjava.io.IOException;2 importjava.nio.charset.StandardCharsets;3 importjava.nio.file.Files;4 importjava.nio.file.Paths;5 import java.util.*;6
7
8 public classOptionalTest {9 public static void main(String[] args) throwsIOException {10 String contents = newString (adAllBytes ( (")), StandardCharsets.UTF_8);12 List wordList = Arrays.asList (contents.split ("\PL+"));13
14 Optional optionalValue =wordList.stream ()15 .filter (s -> s.contains ("filter"))16 .findFirst ();17 System.out.println (Else ("No word")+" contains filter");18 //filtering contains filter
19
20 Optional optionalString pty ();21 String result = Else ("N/A");22 System.out.println ("result:"+result);23 //result:N/A
24 result = ElseGet (() -&Default ().getDisplayName ());25 System.out.println ("result:"+result);26 //result:English (China)
27
28 try{29 result = ElseThrow (IllegalStateException::new);30 System.out.println ("result:"+result);31 } catch(Throwable t){32 t.printStackTrace ();33 }34 /*java.lang.IllegalStateException35 at java.base/java.ElseThrow(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 results = new HashSet();44 optionalValue.ifPresent (results::add);45 Optional 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 //pty
53 System.out.println (inverse(0.0).flatMap(OptionalTest::squareRoot));54 //pty
55 Optional result2 = Optional.of (-4.0)56 .flatMap (OptionalTest::inverse).flatMap (OptionalTest::squareRoot);57 System.out.println (result2);58 //pty
59 }60
61
62 private static Optionalinverse(Double x) {63 return x == 0 ? pty () : Optional.of (1 /x ) ;64 }65
66 private static OptionalsquareRoot(Double x) {67 return x < 0 ?pty () : Optional.of (Math.sqrt (x));68 }69
70 }
八、收集结果
1 importjava.io.IOException;2 importjava.nio.charset.StandardCharsets;3 importjava.nio.file.Files;4 importjava.nio.file.Paths;5 import java.util.*;6 importjava.util.stream.Collectors;7 importjava.util.stream.Stream;8
9 public classCollectingResult {10 public static Stream noVowels() throwsIOException {11 String contents = newString (adAllBytes ( (")), StandardCharsets.UTF_8);13 List wordList = Arrays.asList (contents.split ("\PL+"));14 Stream words =wordList.stream ();15 return words.map (s -> s.replaceAll ("[aeiouAEIOU]",""));16 }17 public static void show(String label,Setset){18 System.out.print(label + ":" Class ().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) throwsIOException {25 /**
26 * java.util.stream.BaseStream27 * Iterator iterate()28 * 产生一个用于获取当前流中各个元素的迭代器。这是一个终结操作。29 */
30 Iterator iter = Stream.iterate (0,n -> n+1).limit (10)31 .iterator ();32 while(iter.hasNext ())33 System.out.println ( ());34 /**
35 *Object[] toArray()36 * A[] toArray(IntFunction 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.Stream56 * R collect(Collector super T,A,R> collector)57 * 使用给定的收集器来收集当前流中的元素。Collectors类有用于多种收集器的工厂方法。58 */
59 /**
60 * java.util.stream.Collectors61 * static Collectors> toList()62 * static Collectors> toSet()63 * 产生一个将元素收集到列表或集中的收集器64 */
65 Set noVowelSet =noVowels ()66 .Set());67 show ("noVowelSet",noVowelSet);68 /**
69 * static > Collector toCollection(Supplier collectionFactory)70 * 产生一个将元素收集到任意集合中的收集器。可以传递一个诸如TreeSet::new的构造器引用。71 */
72 TreeSet noVowelTreeSet =noVowels ()73 .Collection(TreeSet::new));74 show ("noVowelTreeSet",noVowelSet);75 /**
76 * static Collector joining()77 * static Collector joining(CharSequence delimiter)78 * static Collector 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 Collector summarizingInt(ToIntFunction super T> mapper)92 * static Collector summarizingLong(ToLongFunction super T> mapper)93 * static Collector 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 Average ();100 double maxWordLength Max ();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.Stream107 * void forEach(Consumer super T> action)108 * 在流的每个元素上调用action。这是一种终结操作。109 */
110 noVowels ().limit (10).forEach (System.out::println);111 }112 }
九、收集到映射表中、群组和分区
1 import java.util.*;
2 importjava.util.function.Function;
3 importjava.util.stream.Collectors;
4 importjava.util.stream.Stream;
5
6 /**
7 * Created by kong on 24/11/2017.
8 */
9
10 public classCollectingIntoMaps {
11 public static classPerson{
12 private intid;
13 privateString name;
14
15 public Person(intid, String name) {
16 this.id =id;
17 this.name =name;
18 }
19
20 public intgetId() {
21 returnid;
22 }
23
24 publicString getName() {
25 returnname;
26 }
27
28 @Override
29 publicString toString() {
30 return "Person{" +
31 "id=" + id +
32 ", name='" + name + ''' +
33 '}';
34 }
35 }
36 public static Streampeople(){
37 return Stream.of (new Person (1001,"Peter"),
38 new Person (1002,"Paul"), new Person (1003,"Mary"));
39 }
40
41 public static voidmain(String[] args) {
42
43
44 /**
45 *
46 * static Collector>
47 * toMap(Function super T, ? extends K> keyMapper,
48 * Function super T, ? extends U> valueMapper,BinaryOperator mergeFunction)
49 *
50 * static > Collector
51 * toConcurrentMap(Function super T,? extends K> keyMapper,
52 * Function super T,? extends U> valueMapper,BinaryOperator
53 * mergeFunction,Supplier mapSupplier)
54 *
55 * 产生一个收集器,它会产生一个映射表或并发映射表。ketMapper和valueMapper函数会应用于每个收集到的元素上,
56 * 从而在所产生的映射表中生成一个键/值项。默认情况下,当两个元素产生相同的键时,会抛出一个IllegalStateException异常。
57 * 你可以提供一个mergeFunction来合并具有相同键的值。默认情况下,其结果是一个HashMap或ConcurrentHashMap。
58 * 你可以提供一个mapSupplier,它会产生所期望的映射表实例。
59 */
60 Map idToName =people ().collect (
Map (Person::getId,Person::getName));
62 System.out.println ("idToName:" +idToName);
63 //idToName:{1001=Peter, 1002=Paul, 1003=Mary}
64
65
66 Map idToPerson =people ().collect (
Map (Person::getId, Function.identity ()));
68 System.out.println ("idToPerson:" + Class ().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 (
Map (Person::getId,Function.identity (),(existingValue,newValue) ->{
74 throw newIllegalStateException ();
75 }, TreeMap::new));
76 System.out.println ("idToPerson:" Class ().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 locales =Stream.of (AvailableLocales ());
81 Map languageNames llect (Map (
82 Locale::getDisplayLanguage,l -&DisplayLanguage (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 (AvailableLocales ());
89 Map> countryLanguageSets llect (
Map (
91 Locale::getDisplayCountry,l ->Collections.singleton (l.getDisplayLanguage ()),
92 (a,b) -> { //union of a and b
93 Set union = new HashSet<>(a);
94 union.addAll (b);
95 returnunion;
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 Collector>>
103 * groupingBy(Function super T,? extends K> classifier)
104 *static Collector>>
105 * groupingByConcurrent(Function super T,? extends K> classifier)
106 * 生成一个收集器,该收集器生成一个map或并发映射,
107 * 其键是将classifier应用于所有收集的元素的结果,其值是具有相同键的元素构成的一个个列表。
108 *
109 * static Collector>>
110 * partitioningBy(Predicate super T> predicate)
111 * 生成一个收集器,它生成一个映射表,其键是true/false,其值是实现/不满足断言的元素构成的列表。
112 */
113 locales =Stream.of (AvailableLocales ());
114 //将具有相同特性的值群聚成组是非常见的,并且groupingBy方法直接就支持它。
115 Map> countryToLocales llect (
upingBy (Locale::getCountry));
117 List swissLocales = ("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 (AvailableLocales ());
127 Map> englishAndOtherLocales llect (
128 Collectors.partitioningBy (l -> l.getLanguage ().equals ("en")));
129 List englishLocales = (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 }
十、下游收集器
groupingBy方法生成一个映射表,它的每个值都是一个列表。如果您想以某种方式处理这些列表,就需要提供一个“下游收集器”。例如,如果你想要获得集而不是而不是列表,那么可以使用前一节中看到的Set收集器:
Map> countryToLocaleSet = llect(
grounpingBy(Locale::getCountry,toSet()));
1 importjava.io.IOException;
2 importjava.nio.file.Files;
3 importjava.nio.file.Paths;
4 import java.util.*;
5 importjava.util.stream.Stream;
6
7 import static java.util.stream.Collectors.*;
8
9 public classDownstreamCollectors {
10 public static classCity{
11 privateString name;
12 privateString state;
13 private intpopulation;
14
15 public City(String name, String state, intpopulation) {
16 this.name =name;
17 this.state =state;
18 this.population =population;
19 }
20
21 publicString getName() {
22 returnname;
23 }
24
25 publicString getState() {
26 returnstate;
27 }
28
29 public intgetPopulation() {
30 returnpopulation;
31 }
32 }
33
34 public static Stream readCities(String filename) throwsIOException {
35 return Files.lines ( (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) throwsIOException{
40 Stream locales =Stream.of (AvailableLocales ());
41
42 locales =Stream.of (AvailableLocales ());
43 Map> countryToLocaleSet llect (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 Collector counting()
50 * 产生一个可以对收集到的元素进行计数的收集器。
51 */
52 locales =Stream.of (AvailableLocales ());
53 Map countryToLocaleCounts llect (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 Collector summarizingInt(ToIntFunction super T> mapper)
60 * static Collector summarizingLong(ToLongFunction super T> mapper)
61 * static Collector summarizingDouble(ToDoubleFunction super T> mapper)
62 * 产生一个收集器,对mapper应用到收集到的元素上之后产生的值进行计算总和
63 */
64 Stream cities = readCities (");
65 Map stateToCityPopulation llect (groupingBy (
66 City::getState,summarizingInt (City::getPopulation)));
67 System.out.println ("stateToCityPopulation: " +stateToCityPopulation);
68
69 cities = readCities (");
70 Map stateToCityPopulationSummary =cities
71 .collect (groupingBy (
72 City::getState,
73 summarizingInt (City::getPopulation)));
74 System.out.println ("stateToCityPopulationSummary: " +stateToCityPopulationSummary);
75
76
77 /**
78 * static Collector> maxBy(Comparator super T> comparator)
79 * static Collector> minBy(Comparator super T> comparator)
80 * 产生一个收集器,使用comparator指定的排序方法,计数收集到的元素中的最大值和最小值。
81 */
82 cities = readCities (");
83 Map> stateToLongestCityName =cities
84 .collect (groupingBy (
85 City::getState,
86 mapping (City::getName,
87 maxBy (Comparatorparing (String::length)))));
88 System.out.println ("stateToLongestCityName: " +stateToLongestCityName);
89
90 /**
91 * static Collector mapping(Function super T, ? extends U> mapper,
92 * Collector super U, A, R> downstream)
93 * 产生一个收集器,它产生一个映射表,其键是将mapper应用到收集到的数据上而产生的,
94 * 其值是使用downstream收集器收集到的具有相同键的元素
95 */
96 locales =Stream.of (AvailableLocales ());
97 Map> countryToLanguages llect (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 (");
105 Map stateToCityNames =cities
106 .collect (groupingBy (
107 City::getState,
108 reducing ("",City::getName,(s,t) -> s.length () == 0 ?t : s)));
109
110 cities = readCities (");
111 stateToCityNames llect (groupingBy (City::getState,
112 mapping (City::getName, joining(","))));
113 System.out.println ("stateToCityNames: " +stateToCityNames);
114 }
115 }
十一、约简操作
reduce方法是一种用于从流中计算某值的通用机制,其最简单的形式接受一个二元函数,并从前两个元素开始持续应用它。如果该函数是求和函数,那么很容易解释这种机制:
List values = ...;
Optional sum = values.stream().reduce((x, y) - > x + y);
//reduce((x, y) - > x + y)可以写成reduce(Integer::sum);
在上面的情况中,reduce方法会计算v0+v1+v2+...,其中vi是流中的元素。如果流为空,那该方法会返回一个Optional,因为没有任何有效的结果。
十二、基本类型流
我们都是将整数收集到Stream中,将每个整数封装到一个包装器对象中是很低效的。对于其他的基本类型来说,同样的情况也适用于double、float、long、short、char、byte和boolean。流库拥有特殊类型的IntStream、LongStream和DoubleStream,它们直接存储基本类型值,而不使用包装器。如果您想要存储short、char、byte和boolean,使用一个IntStream,而对于float,则使用DoubleStream。使用方式与Stream相似。
1 importjava.io.IOException;
2 importjava.nio.charset.StandardCharsets;
3 importjava.nio.file.Files;
4 importjava.nio.file.Path;
5 importjava.nio.file.Paths;
6 importjava.util.stream.Collectors;
7 importjava.util.stream.IntStream;
8 importjava.util.stream.Stream;
9
10 public classPrimitiveTypeStreams {
11 public static voidshow(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
18 else System.out.print ("...");
19 }
20 System.out.println ();
21 }
22
23 public static void main(String[] args) throwsIOException {
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 values)
31 * 产生一个由当前流中的元素构成的数组
32 * int sum()
33 * OptionalDouble average()
34 * OptionalInt max()
35 * OptionalInt min()
36 * IntSummaryStatistics summaryStatistics()
37 * 产生当前流中元素的总和、平均值、最大值和最小值,或者从中可以获得这些结果所有四种值的对象。
38 *
39 * Stream boxed()
40 * 产生用于当前流中的元素的包装器对象流
41 */
42 IntStream is1 = ate (() -> (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 = (");
53 String contents = newString (adAllBytes (path), StandardCharsets.UTF_8);
54
55 Stream 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 ="uD835uDD46 is the set of octonions.";
61 System.out.println (sentence);
62 //𝕆 is the set of octonions.
63 IntStream codes dePoints ();
64 System.out.println (codes.mapToObj (c -> String.format ("%X",c)).collect (
65 Collectors.joining ()));
66 //1D5462069732074686520736574206F66206F63746F6E696F6E732E
67
68 Stream 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 }
十三、并行流
流使得并行化批量操作变得很容易。这个过程基本上是自动的,但是您需要遵循一些规则。首先,你必须有一个并行流。你可以用Collection.parallelStream()方法从任何集合中获取一个并行流:
Stream parallelWords = words.parallelStream();
此外,parallel方法可以将任何顺序流转换为并行流。
Stream parallelWords = Stream.of(wordArray).parallel();
只要在终结方法执行时流处于并行模式,所有中间流操作都将被并行化。
默认情况下,来自有序集合(数组和列表)、范围、生成器和迭代器产生的流,或者通过调用Stream.sorted产生的流,都是有序的。它们结果是按照原始元素的顺序积累的,并且是完全可预测的。如果您两次运行相同的操作,您将得到完全相同的结果。
排序并不排斥高效的并行化处理。例如,当计算Stream.map(fun)时,可以将流划分为n段,每个段都是并发处理的。然后,结果会按顺序重新组合。
当排序需求被删除时,一些操作可以更有效地并行化。通过在流中调用unordered方法,表明对排序不感兴趣。
为了使并行流运行良好,需要满足许多条件:
数据应该在内存中。必须等待数据到达是非常低效的。
流应该可以被高效的分成若干个子部分。由数组或平衡的二叉树支撑的流可以很好地工作,但是Stream.iterate返回的结果不行。
流操作应该做大量的工作。如果总工作负载不是很大,那么为建立并行计算付出的代价是没有意义的。
流操作不应该阻塞。
换句话说,不要把所有的流都变成并行的流。只有在对已经存在于内存中的数据进行大量的持续计算工作时,才使用并行流。
1 importjava.io.IOException;
2 importjava.nio.charset.StandardCharsets;
3 importjava.nio.file.Files;
4 importjava.nio.file.Paths;
5 importjava.util.Arrays;
6 importjava.util.List;
7 importjava.util.Map;
8 import staticjava.util.unting;
9 import staticjava.util.upingBy;
10 import staticjava.util.upingByConcurrent;
11
12 public classParallelStreams {
13 public static void main(String[] args) throwsIOException{
14
15 /**
16 * java.util.stream.BaseStream>
17 * S parallel()
18 * 产生一个与当前流中元素相同的并行流
19 * S unordered()
20 * 产生一个与当前流中元素相同的无序流
21 *
22 * java.util.Collection
23 * Stream parallelStream()
24 * 用当前集合中的元素产生一个并行流。
25 */
26
27 String contents = newString (adAllBytes (
(")), StandardCharsets.UTF_8);
29 List 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 (String (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 (String (shotWords));
45 //[0, 2, 13, 17, 11, 6, 6, 4, 7, 1]
46
47 //Remedy: Group and count
48 Map 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> result =wordList.parallelStream ().collect (
57 groupingByConcurrent (String::length));
58
59 System.out.println ( (14));
60 //null
61
62 result =wordList.parallelStream ().collect (
63 groupingByConcurrent (String::length));
64
65 System.out.println ( (14));
66 //null
67
68 Map 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 }
本文发布于:2024-01-31 17:30:12,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170669341430200.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |