第二章 Stream API

Stream 是Java8处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,但是将执行操作的时间交给具体实现来决定。例如,如果你希望计算某个方法的平均值,你可以在每个元素上指定调用的方法,从而获得所有值的平均值。你可以使用 Stream API来并行执行操作,使用多线程来计算每一段的总和与数量,再将结果汇总起来


一、从迭代器到Stream操作
1.Stream与集合的区别
(1). Stream 自己不会存储元素,元素可能被存储在底层的集合中,或者根据需要产生
出来
(2). Stream 操作符不会改变源对象,相反,他们会返回一个持有结果的新 Stream
(3). Stream 操作符可能是延迟操作的,即会等到需要结果的时候才执行
2.Stream遵循"做什么,而不是怎么去做"的原则
3.通过三个阶段来建立一个操作流水线
(1).创建一个Stream
(2).在一个或多个步骤中,指定将初始Stream转换为另一个Stream的中间操作
(3).使用一个终止操作来产生一个结果,该操作会强制它之前的延迟操作立即执行
4.并行执行
Stream 的并行执行只需要将 stream() 方法改成 parallelStream() 方法,或者调用 stream parallel() 方法
5.以下是统计书中所有长单词的例子
在Java8之前,处理集合通常会迭代所有元素并对其中的每个进行处理
--------------------------------------------------------------------------------------------------------------------
String contents =new String(Files.readAllBytes(Paths.get("alice.txt"))
,StandardCharsets.UTF_8);
List<String> words=Array.asList(contents.split("[\\P{L}]+"));
int count = 0;
for(String w:words){
if(w.length()>12){
count++;
}
}
--------------------------------------------------------------------------------------------------------------------
而在Java8中可以通过 Stream 实现相同功能的操作
--------------------------------------------------------------------------------------------------------------------
String contents =new String(Files.readAllBytes(Paths.get("alice.txt"))
,StandardCharsets.UTF_8);
List<String> words=Array.asList(contents.split("[\\P{L}]+"));
long count=words. stream() . filter ( w->w.length() >12). count() ;
--------------------------------------------------------------------------------------------------------------------
stream() 方法会为单词列表生成一个 Stream filter() 方法会返回另一个只包含单词长度大于12的 Stream count() 方法会将 Stream 化简为一个结果
Stream 操作不会按照元素的调用顺序执行,在本例中,只有在 count() 被调用的时候才会执行 Stream 操作。当 count() 方法需要第一个元素时, filter() 方法会开始请求各个元素,知道找到一个长度大于12的元素

二、创建Stream
1.通过Java8在Collection接口中添加的 stream() 方法,可以将任何集合转化为一个 Stream
2.如果是一个数组,可以通过静态的 Stream .of() 方法将其转化为一个 Stream
3. Stream .of() 方法接收可变长度的参数,因此可以构建一个含有任意个参数的 Stream
--------------------------------------------------------------------------------------------------------------------
Stream<String> song = Stream . of ("gently","down","the","stream");
--------------------------------------------------------------------------------------------------------------------
4.可以使用Arrays. stream(array,from,to) 方法将数组的一部分转化为 Stream
5.使用 Stream . empty() 方法可以创建一个不含任何元素的 Stream
--------------------------------------------------------------------------------------------------------------------
Stream<String> silence= Stream . empty() ;
--------------------------------------------------------------------------------------------------------------------
6. Stream 接口有两个用来创建无限 Stream 的静态方法
(1).generate方法接收一个无参数的函数,当需要一个Stream值时,可以调用该
方法来产生一个值
--------------------------------------------------------------------------------------------------------------------
Stream<String> echos= Stream . generate ( ()->"Echo" );
Stream<Double> randoms= Stream . generate ( Math::random );
--------------------------------------------------------------------------------------------------------------------
(2).iterate方法接收一个"种子"值和一个函数作为参数,并且会对之前的值重复应
用该函数,如下例,获取0 1 2 3 4 ……的无限序列
--------------------------------------------------------------------------------------------------------------------
Stream<BigInteger> integers =
Stream . iterate (BigInteger.ZERO, n->n.add(BigInteger.ONE) );
--------------------------------------------------------------------------------------------------------------------
7.Java8中添加了许多能够产生 Stream 的方法,举两个例子
(1). Pattern 类添加了一个 splitAsStream 的方法,能按正则表达式对
CharSequence对象进行分隔
--------------------------------------------------------------------------------------------------------------------
Stream<String> words = Pattern .compile("[\\P{L}]+"). splitAsStream (contents);
--------------------------------------------------------------------------------------------------------------------
(2). Files 类添加了 lines 方法,会返回一个包含文件中所有行的 Stream Stream
口有一个父接口 AutoCloseable ,当在某个Stream上调用 close 方法时候,底层的
文件也会被关闭。一般为了确保关闭文件,最好使用Java7中提供的
try-with-resources语句
--------------------------------------------------------------------------------------------------------------------
try(Stream<String> lines=Files.lines(path)){
……
}
--------------------------------------------------------------------------------------------------------------------

三、fileter、map和flatMap方法
流转换是指从一个流中读取数据,并将转换后的数据写入到另一个流中
1.filter
filter() 方法会残生一个新的流,其中包含符合某个特定条件的所有元素
filter() 方法的参数是一个Predicate<T>对象,即一个从T到boolean的函数
--------------------------------------------------------------------------------------------------------------------
List<String> wordList=……;
Stream<String> words=wordList.stream();
Stream<String> longWords=words. filter (w->w.length()>12);
--------------------------------------------------------------------------------------------------------------------
2.map
当需要对一个流中的值进行某种形式的转换时,可以考虑使用 map() 方法,并传递给它一个执行转换的函数
--------------------------------------------------------------------------------------------------------------------
List<String> wordList=……;
Stream<String> words=wordList.stream();
Stream<String> lowercaseWords=words. map (String::toLowerCase);
Stream<Character> firstChars=words. map (s->s.charAt(0));
--------------------------------------------------------------------------------------------------------------------
3.flatMap
假设有一个函数,给他一个字符串,返回一个包含多个值的流,如果将该函数传递给 map() ,将会产生一个包含多个流的流
--------------------------------------------------------------------------------------------------------------------
public static Stream<Character> characterStream(String s){
List<Character> result=new ArrayList<>();
for(char c:s.toCharArray()){
result.add(c);
}
return result.stream();
}
Stream<Stream<Character>> result=words. map (w->characterStream(w));
//result为[...,['y','o','u'],[j'','b','k']]
--------------------------------------------------------------------------------------------------------------------
如果想要获取一个只包含字符串的流,需要使用 flatMap 方法而不是 map 方法
--------------------------------------------------------------------------------------------------------------------
public static Stream<Character> characterStream(String s){
List<Character> result=new ArrayList<>();
for(char c:s.toCharArray()){
result.add(c);
}
return result.stream();
}
Stream<Stream<Character>> result=words. flatMap (w->characterStream(w));
//result为[...,"y","o","u","j","b","k"]
--------------------------------------------------------------------------------------------------------------------

四、提取子流和组合流
1. limit(n) :返回一个包含前n个元素的新流,如果原始流的长度小于n,则会返回原始的流
--------------------------------------------------------------------------------------------------------------------
Stream<Double> randoms= Stream . generate ( Math::random ). limit (100);
--------------------------------------------------------------------------------------------------------------------
2. skip(n) :丢弃前n个元素
--------------------------------------------------------------------------------------------------------------------
Stream<BigInteger> integers= Stream . iterate (BigInteger.ZERO
, n->n.add (BigInteger.ONE)). limit (100);
--------------------------------------------------------------------------------------------------------------------
3. Stream . concat() :将两个流连接到一起,第一个流的长度不应该是无限的
--------------------------------------------------------------------------------------------------------------------
Stream<String> combined= Stream . concat ( Stream . of ("o","k"), Stream . of ("k","o"));
--------------------------------------------------------------------------------------------------------------------
4. peak(funk) :产生另一个与原始流具有相同元素的流,但每次获取一个元素时,都会调用一个函数
--------------------------------------------------------------------------------------------------------------------
Object[] powers= Stream . iterate (1.0,p->p*2). peek ( e->System.out.println(e) )
. limit (20).toArray();
--------------------------------------------------------------------------------------------------------------------

五、有状态的转换
上文中介绍的流转换都是无状态的,Java8中提供了有状态的转换
1. distinct() :根据原始流中的元素返回一个具有相同的顺序、抑制了重复元素的新流
--------------------------------------------------------------------------------------------------------------------
Stream<String> uniqueWords= Stream . of ("merrily","merrily","gently"). distinct() ;
--------------------------------------------------------------------------------------------------------------------
2. sorted() :遍历整个流,产生并返回一个新的已排序的流,Java8中提供了多个sorted方法,其中一个用于其中元素实现了Comparable接口的流,另一个接收一个Comparator对象
--------------------------------------------------------------------------------------------------------------------
Stream<String> longestFirst=words. sorted (Comparator.comparing( String::length )
. reversed() );
--------------------------------------------------------------------------------------------------------------------

六、简单的聚合方法
聚合方法都是终止操作,当一个流应用了终止操作后,它就不能再应用其他的操作了
1. count() :返回流中元素的总数
2. max() min() :返回流中最大值和最小值
--------------------------------------------------------------------------------------------------------------------
Optional<String> largest=words. max ( String::compareToIgnoreCase );
if(largest. isPresent() ){
System.out.println("largest:"+largest.get());
}
--------------------------------------------------------------------------------------------------------------------
3. findFirst() :返回非空集合中的第一个值,通常与filter方法结合使用
--------------------------------------------------------------------------------------------------------------------
Optional<String> startsWithQ=words. filter ( s->s.startsWith("Q") ). findFirst() ;
--------------------------------------------------------------------------------------------------------------------
4. findAny() :返回集合中的任意一个值,并行执行时十分有效
--------------------------------------------------------------------------------------------------------------------
Optional<String> startsWithQ=words. parallel() . filter ( s->s.startsWith("Q") ). findAny() ;
--------------------------------------------------------------------------------------------------------------------
这些方法会返回一个 Optional <T>值,它可能会封装返回值,也可能标识没有返回(当流为空)Optional类型是一种更好的标识缺少返回值的方式
5. anyMatch() :查看流中是否有匹配元素,该方法接收一个 predicate 参数,并行执行时十分有效
--------------------------------------------------------------------------------------------------------------------
boolean b=words. parallel() . anyMatch(s->s.startsWith("Q")) ;
--------------------------------------------------------------------------------------------------------------------
6. allMatch() noneMatch() :查看流中是否所有元素匹配或没有元素匹配,仍可以通过并行执行来提高速度

七、Optional类型
Optional<T>对象或者是对一个T类型对象的封装,或者表示不是任何对象。因为它不会返回null,所以在正确使用的前提下比一般指向T类型的引用更安全
get() :如果存在被封装对象,则返回该对象,否则抛出NoSuchuElementException异常
isPresent() :返回一个boolean值,反映Optional<T>对象是否有值
1.使用Optional值
高效使用Optional的关键在于,使用一个或者接收正确值、或者返回另一个替代值的方法
(1). ifPresent() :接收一个函数,如果存在可选值,将该值传给函数,否则不会进行任何操作
--------------------------------------------------------------------------------------------------------------------
optionalValue. ifPresent ( results::add );
optionalValue. ifPresent ( v->results.add(v) );
--------------------------------------------------------------------------------------------------------------------
(2). map() :接收一个函数,功效与 ifPresent() 一致,但是会返回Optional<Boolean>
值可能为true、false或空
--------------------------------------------------------------------------------------------------------------------
optionalValue. map ( results::add );
--------------------------------------------------------------------------------------------------------------------
(3). orElse() orElseGet() orElseThrow()
--------------------------------------------------------------------------------------------------------------------
String result=optionalString. orElse ("");
String result=optionalString. orElseGet ( ()->System.getProperty("user.dir") );
String result=optionalString. orElseThrow ( NosuchElementException::new );
--------------------------------------------------------------------------------------------------------------------
2.创建可选值
(1).可以通过 Optional . of (result)或者 Optional . empty ()来创建一个 Optional 对象
--------------------------------------------------------------------------------------------------------------------
public static Optional<Double> inverse(Double x){
return x==0? Optional . empty() : Optional . of(1/x) ;
}
--------------------------------------------------------------------------------------------------------------------
(2). Optional . ofNullable() :null值和可选值之间的桥梁,如果obj不为null, Optional . ofNullable (obj)会返回 Optional . of (obj),否则返回 Optional . empty()
3.使用flatMap组合可选值函数
假设有一个返回 Optional <T>的方法f,并且目标类型T有一个会返回 Optional <U>的方法g,如果都是普通的方法,我们可能会考虑通过调用s.f().g()将他们组合起来,但是这种组合在这里是行不通的,因为s.f()方法返回的是 Optional <T>而不是T。此时可以调用
optional<U> =s.f(). flatMap ( T::g );
也就是说 flatMap() 方法可以通过展开方法所返回的流,将两个方法组合起来使用

八、聚合操作
如果希望对元素求和,或者以其他方式将流中的元素组合为一个值,可以使用聚合方法 reduce()
--------------------------------------------------------------------------------------------------------------------
Stream<Integer> values=……;
Optional<Integer> sum=values. reduce ( (x,y)->x+y );//values. reduce ( Integer::sum )
//如果流为空,则无法产生有效的结果,该方法会返回一个 Optional
--------------------------------------------------------------------------------------------------------------------
1.一般情况下,聚合方法有一个聚合操作op,该聚合会产生v 0 op v 1 op v 2 op ……,其中v i op v i+1 就表示我们编写的函数调用op(v i ,v i+1 ) ,该操作应该是联合的,即与你组合元素的顺序无关。在数学定义中(x op y) op z=x op (y op z),这样就允许通过并行流进行有效的聚合。减法是一个非联合操作的例子,例如:(6-3)-2≠6-(3-2)
2.通常,如果有一个标识e使得e op x=x,那么你就可以使用该元素作为计算的起点:
--------------------------------------------------------------------------------------------------------------------
Integer sum=values. reduce (0, (x,y)->x+y );
--------------------------------------------------------------------------------------------------------------------
此时当流为空时会返回标识符值,不需要再去处理Optional类
2.假设有一个包含多个对象的流,并且希望对他们的某个属性进行求和,例如求一个流中所有字符串的总长度。这时,你无法使用聚合方法的简单形式,因为它需要一个函数(T,T)->T,其中参数和结果的类型要求是一样的,然而当前这两个类型是不同的。流中元素的类型是String,但是累加结果的类型是整数。对于这种情况,应该使用聚合方法的另一种形式。
首先,要提供一个"累加器"函数(total,word)->total+word.length()。该函数会被重复调用,形成累加值。但是当并行计算时,会产生多个累加值,因此需要提供第二个函数将它们再累加起来
--------------------------------------------------------------------------------------------------------------------
int result=words. reduce (0,
(total,word)->total+word.length() ,
(total1,total2)->total1+total2 );
--------------------------------------------------------------------------------------------------------------------
实际中可能不会大量地使用聚合方法,更简单的方法是映射到一个数字流上,并使用它的方法之一来计算总和、最大值或者最小值。如上述例子中,可以调用words. mapToInt ( String::length ). sum() ,由于不会调用自动封箱和拆箱,所以效率更高,而且更简单

九、收集结果
当处理完流后,想查看结果我们可以通过以下方式
1. interator() :返回一个迭代器
2. toArray() :返回一个Object[]数组,如果希望得到一个正确类型的数组 ,可以将类型传递给数组的构造函数
--------------------------------------------------------------------------------------------------------------------
String[] result=words. toArray ( String[]::new );
--------------------------------------------------------------------------------------------------------------------
3. collect() :返回一个可以记录个数和总和的对象,如集合、StringBuilder,该方法接收三个参数:
(1).一个能创建目标类型实例的方法,例如HashSet的构造函数
(2).一个将元素添加到目标中的方法,例如一个add方法
(3).一个将两个对象整合到一起的方法,例如addAll方法
--------------------------------------------------------------------------------------------------------------------
HashSet<String>result=
stream. collect ( HashSet::new , HashSet::add , HashSet::addAll );
--------------------------------------------------------------------------------------------------------------------
然而实际中并不一定需要传递这么多参数,因为 Collector 接口已经为我们提供了这三个方法并且 Collectors 类还为常用的收集类型提供了各个工厂方法,因此:
将一个流收集到一个list或者set中
--------------------------------------------------------------------------------------------------------------------
List<String> listResult=stream. collect ( Collectors . toList() );
Set<String> setResult=stream. collect ( Collectors . toSet() );
//若要控制得到的集合类型可以通过如下方式
TreeSet<String> treeSetResult=
stream. collect ( Collectors . toCollection ( TreeSet::new ));
--------------------------------------------------------------------------------------------------------------------
将流中所有字符串连接并收集起来
--------------------------------------------------------------------------------------------------------------------
String result=stream. collect ( Collectors . joining() );
//添加分隔符","
String splitResult=stream. collect ( Collectors . joining(",") );
--------------------------------------------------------------------------------------------------------------------
如果流包含字符串以外的对象,你首先需要将他们转换为字符串
--------------------------------------------------------------------------------------------------------------------
String result=stream. map ( Object::toString ). collect ( Collectors . joining(",") );
--------------------------------------------------------------------------------------------------------------------
如果希望将流的结果聚合为一个总和、平均值、最大值或者最小值,那么请使用 Collectors summarizing(Int|Long|Double) 方法中的一种,这些方法会接收一个将流对象映射为一个数字的函数,并产生一个 (Int|Long|Double)SummaryStatistics 类型的结果,其中包含了获取总和、平均值、最大值和最小值的方法
--------------------------------------------------------------------------------------------------------------------
IntSummaryStatistics summary=
words. collect ( Collectors . summarizingInt ( String::length ));
double average = summary. getAverage() ;
long count = summary. getCount() ;
int max = summary. getMax() ;
int min = summary. getMin() ;
long sum = summary. getSum() ;
--------------------------------------------------------------------------------------------------------------------
4. forEach forEachOrdered
forEach(func) 方法接受一个函数,这个函数会应用到流中的每个元素上,在一个
并行流上,可能会以任意顺序来访问元素。如果希望按照流的顺序来执行他们,那么
请调用 forEachOrdered ,当然这样会放弃大多数并行计算所能带来的好处
forEach forEachOrdered 方法都是终止操作,在调用它们之后就不能在使用这
个流了,如果希望还能继续使用这个流,可以使用 peak 方法(四)
map 方法不同的是 map 方法是对流中的元素进行处理,而 forEach
forEachOrdered 是对流中的数据进行运用

十、将结果收集到Map中
如果希望将流中的元素收集到一个map中,可以通过 Collectors . toMap() 方法
1.通常,该方法有两个参数,分别用来生成map的键和值
--------------------------------------------------------------------------------------------------------------------
Map<Integer,Person> idToName=
people. collect ( Collectors . toMap (Person::getId,Person::getName));
--------------------------------------------------------------------------------------------------------------------
2.如果有多个元素拥有相同的键,那么收集方法会抛出一个IllegalStateException异常,可以通过第三个函数参数,根据已有的值和新值来决定键的值,从而重写该行为
--------------------------------------------------------------------------------------------------------------------
Stream<Locale> locales = Stream . of (Locale. getAvailableLocales ());
Map<String, String> collect = locales. collect (
Collectors . toMap (
l -> l.getDisplayLanguage(),
l -> l.getDisplayLanguage(l),
(existingValue, newValue) -> existingValue
));
--------------------------------------------------------------------------------------------------------------------
3.如果你需要指定Map集合的类型,需要提供一个构造函数作为第4个参数
--------------------------------------------------------------------------------------------------------------------
Map<Integer,Person> idToName=
people. collect ( Collectors . toMap (Person::getId,Person::getName,
(existingValue,newValue)->{throw new IllegalStateException();},
TreeMap::new ));
--------------------------------------------------------------------------------------------------------------------
4.如果希望通过一个键来获取多个值,例如希望知道指定国家中的所有语言,那么就需要一个Map<String,Set<String>>对象,可以将每个国家中的各种语言存储到单独的集合中,当发现指定国家的一种新语言时,我们就将已有值和新值组合成一个新的集合
--------------------------------------------------------------------------------------------------------------------
Map<String,Set<String>> countryLanguageSets=locales. collect (
Collectors . toMap (
l->l.getDisplayCountry(),
l-> Collections . singleton (l.getDisplayLanguage()),
(a,b)->{
Set<String> r= new HashSet<>(a);
r.addAll(b);
return r;
}
));
--------------------------------------------------------------------------------------------------------------------
5.对于toMap方法的每种形式,都有一个对应的toConcurrentMap方法,用来产生一个并发的map。再并发收集过程中应当只使用一个并发的map,当在并行流中使用并发map时,一个共享的map要比合并map效率更高,但是当然,使用共享的map无法得到有序的结果

十一、分组和分片
1.具有相同特性的值进行分组是一个很常见的任务,可以直接使用 groupingBy 方法
--------------------------------------------------------------------------------------------------------------------
Map<String,List<Locale>> countryToLocales=locales. collect (
Collectors . groupingBy ( Locale::getCountry ));
List<Locale> swissLocales=countryToLocales.get("CH");
--------------------------------------------------------------------------------------------------------------------
Locale::getCountry 是进行分组的分类函数
如果调用 groupingByConcurrent 方法,会获得一个并发map,当用于并行流时可以并发的插入值。这同 toConcurrentMap 方法是完全类似的
2.当分类函数是一个predicate函数(即返回一个布尔值的函数)时,流元素会被分为两组列表:一组是函数会返回true的元素,另一组返回false的元素。
在这种情况下,使用 partitioningBy 比使用 groupingBy 更有效率
--------------------------------------------------------------------------------------------------------------------
Map<Boolean, List<Locale>> en =
locales. collect ( Collectors . partitioningBy (l -> l.getLanguage().equals("en")));
--------------------------------------------------------------------------------------------------------------------
3.downstream收集器
(1). Collectors . toSet()
如果希望指定值得类型,需要提供一个"downstream收集器",例如希望map得值
是set而不是list可以提供 Collectors . toSet 方法
--------------------------------------------------------------------------------------------------------------------
Map<String,Set<Locale>> countryToLocales=
locales. collect ( Collectors . groupingBy (Locale::getCountry, Collectors . toSet() ));
--------------------------------------------------------------------------------------------------------------------
(2). Collectors . counting() :返回所收集元素的总个数
--------------------------------------------------------------------------------------------------------------------
Map<String, Long> collect =
locales. collect ( Collectors . groupingBy (Locale::getCountry, Collectors . counting() ));
--------------------------------------------------------------------------------------------------------------------
(3). Collectors . summing (Int|Long|Double)
该方法接受一个函数作为参数参数,它会将该函数应用到downstream元素中,并生成它们的求和
--------------------------------------------------------------------------------------------------------------------
Map<String,Integer> stateToCityPopulation=
cities. collect ( Collectors. groupingBy (City::getState,
Collectors . summingInt (City::getPopulation)));
--------------------------------------------------------------------------------------------------------------------
(4). Collectors . maxBy() | minBy()
该方法接受一个比较器,并声称downstream元素中的最大值和最小值
--------------------------------------------------------------------------------------------------------------------
Map<String,Integer> stateToCityPopulation=
cities. collect ( Collectors. groupingBy (City::getState,
Collectors . maxBy ( Comparator . comparing (City::getPopulation))));
--------------------------------------------------------------------------------------------------------------------
(5). Collectors . mapping()
该方法将一个函数应用到downstream结果上,并且需要另一个收集器来处理结
--------------------------------------------------------------------------------------------------------------------
Map<String,Integer> stateToCityPopulation=
cities. collect ( Collectors. groupingBy (City::getState,
Collectors . mapping (City::getName,
Collectors . maxBy ( Comparator . comparing (String::length))));
--------------------------------------------------------------------------------------------------------------------
在10.4节中,获取指定国家所有语言集合时,我们用了toMap方法,这里可以给
出一个更好的解决方案
--------------------------------------------------------------------------------------------------------------------
Map<String, Set<String>> collect =
locales. collect ( Collectors . groupingBy (l -> l.getDisplayCountry(),
Collectors . mapping (l -> l.getDisplayLanguage(), Collectors . toSet() )));
--------------------------------------------------------------------------------------------------------------------
(6).如果grouping或者mapping函数的返回类型是int、long或者double,你可以将元素收集到一个summary statics对象中
--------------------------------------------------------------------------------------------------------------------
Map<String,Integer> stateToCityPopulationSummary=
cities. collect ( Collectors. groupingBy (City::getState,
Collectors . summarizingInt (City::getPopulation)));
--------------------------------------------------------------------------------------------------------------------
(7) Collectors . reducing() //有Stream.reduce方法后,基本很少使用
该方法会对downstream元素进行一次普通的聚合操作,有三种形式
reducing(binaryOperator)
reducing(identity,binaryOperator)
reducing(identity,mapper,binaryOperator)
第一种形式中identity是null,第三中行驶中,会应用mapper函数并聚合其值
--------------------------------------------------------------------------------------------------------------------
Map<String,Integer> stateToCityName=
cities. collect ( Collectors. groupingBy (City::getState,
Collectors . reducing ("",City::getName,(s,t)->s.length()==0?t:s+","+t)));
--------------------------------------------------------------------------------------------------------------------

十二、原始类型流
1.Stream API提供了 IntStream LongStream DoubleSream 等类型,专门用来存储原始类型值,不必使用包装。如果你想要存储short、char、byte和boolean类型的值,可以使用 IntStream ;如果要存储float类型的值,可以使用 DoubleStream
--------------------------------------------------------------------------------------------------------------------
IntStream stream= IntStream . of (1,1,2,3);
--------------------------------------------------------------------------------------------------------------------
2.IntStream和LongStream还拥有静态方法 range ranggeClosed ,用来产生步进为1的一个整数范围
--------------------------------------------------------------------------------------------------------------------
IntStream stream= IntStream . range (0,100); //不包括上限
IntStream stream= IntStream . rangeClosed (0,100); //包括上限
--------------------------------------------------------------------------------------------------------------------
3.可以通过 mapToInt mapToLong 或者 mapToDouble 方法将一个对象流转换为一个原始类型流
--------------------------------------------------------------------------------------------------------------------
IntStream stream= Stream . of (1,2,3,4). mapToInt() ;
--------------------------------------------------------------------------------------------------------------------
4.可以通过boxed()方法将一个原始类型流转换为一个对象流
--------------------------------------------------------------------------------------------------------------------
Stream <Integer> integers= IntStream . range (0,100). boxed() ;
--------------------------------------------------------------------------------------------------------------------
5.CharSequence接口有 codePoints chars 方法,可以生成包含字符串Unicode代码的流,或者是包含UTF-16编码的代码单元的 IntStream
--------------------------------------------------------------------------------------------------------------------
String sentence="\uD835\uDD46 is the set of octonions.";
IntStream codes=sentence. codePoints() ;
--------------------------------------------------------------------------------------------------------------------
6.原始类型流与对象流的方法调用有以下几点区别:
  • toArray方法会返回一个原始类型的数组
  • 产生Optional结果的方法会返回一个OptionalIntOptionalLong或者OptionalDouble类型,这些类与Optional类类似,但是没有get方法,而使用对应的getAsInt()getAsLong()getAsDouble()来代替
  • 方法sumaveragemaxmin会返回总和、平均值、最大值和最小值,而对象流中没有这些方法
  • summaryStatistics方法会产生一个IntSummaryStatisticsLongSummaryStatistics或者DoubleSummaryStatistics对象
7.Java8中, Random 类提供了 ints longs doubles 方法,用来返回包含随机数字的原始类型流

十三、函数式接口
1.有时在查看javadoc文档时,会看到类似:
Stream<T> filter( Predicate <? super T> predicate)
Predicate 是一个接口,只含有一个返回boolean值的非默认方法,一般只需要记
住Predicate是一个返回boolean值的函数就行了
--------------------------------------------------------------------------------------------------------------------
public interface Predicate{
boolean test(T argument);
}
--------------------------------------------------------------------------------------------------------------------
2.在Stream API中常见的函数式接口
接口
参数类型
返回类型
描述
Supplier<T>
T
提供一个T类型的值
Consumer<T>
T
void
处理一个T类型的值
BiConsumer<T,U>
T,U
void
处理T类型和U类型的值
Predicate<T>
T
boolean
计算boolean值的函数
ToIntFunction<T>
ToLongFunction<T>
ToDoubleFunction<T>
T
int
long
double
分别计算int、long、
double值的函数
IntFunction<R>
LongFunction<R>
DoubleFunction<R>
int
long
double
R
参数分别为int、long或
double类型的函数
Function<T,R>
T
R
一个参数为类型为T的
函数
BiFunction<T,U,R>
T,U
R
一个参数类型为T和U的
函数
UnaryOperator<T>
T
T
对类型T进行的一元操作
BinaryOperator<T>
T,T
T
对类型T进行的二元操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值