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 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 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 song =
Stream
.
of
("gently","down","the","stream");
--------------------------------------------------------------------------------------------------------------------
4.可以使用Arrays.
stream(array,from,to)
方法将数组的一部分转化为
Stream
5.使用
Stream
.
empty()
方法可以创建一个不含任何元素的
Stream
--------------------------------------------------------------------------------------------------------------------
Stream silence=
Stream
.
empty()
;
--------------------------------------------------------------------------------------------------------------------
6.
Stream
接口有两个用来创建无限
Stream
的静态方法
(1).generate方法接收一个无参数的函数,当需要一个Stream值时,可以调用该
方法来产生一个值
--------------------------------------------------------------------------------------------------------------------
Stream echos=
Stream
.
generate
(
()->"Echo"
);
Stream randoms=
Stream
.
generate
(
Math::random
);
--------------------------------------------------------------------------------------------------------------------
(2).iterate方法接收一个"种子"值和一个函数作为参数,并且会对之前的值重复应
用该函数,如下例,获取0 1 2 3 4 ……的无限序列
--------------------------------------------------------------------------------------------------------------------
Stream integers =
Stream
.
iterate
(BigInteger.ZERO,
n->n.add(BigInteger.ONE)
);
--------------------------------------------------------------------------------------------------------------------
7.Java8中添加了许多能够产生
Stream
的方法,举两个例子
(1).
Pattern
类添加了一个
splitAsStream
的方法,能按正则表达式对
CharSequence对象进行分隔
--------------------------------------------------------------------------------------------------------------------
Stream words =
Pattern
.compile("[\\P{L}]+").
splitAsStream
(contents);
--------------------------------------------------------------------------------------------------------------------
(2).
Files
类添加了
lines
方法,会返回一个包含文件中所有行的
Stream
。
Stream
接
口有一个父接口
AutoCloseable
,当在某个Stream上调用
close
方法时候,底层的
文件也会被关闭。一般为了确保关闭文件,最好使用Java7中提供的
try-with-resources语句
--------------------------------------------------------------------------------------------------------------------
try(Stream lines=Files.lines(path)){
……
}
--------------------------------------------------------------------------------------------------------------------
三、fileter、map和flatMap方法
流转换是指从一个流中读取数据,并将转换后的数据写入到另一个流中
1.filter
filter()
方法会残生一个新的流,其中包含符合某个特定条件的所有元素
filter()
方法的参数是一个Predicate对象,即一个从T到boolean的函数
--------------------------------------------------------------------------------------------------------------------
List wordList=……;
Stream words=wordList.stream();
Stream longWords=words.
filter
(w->w.length()>12);
--------------------------------------------------------------------------------------------------------------------
2.map
当需要对一个流中的值进行某种形式的转换时,可以考虑使用
map()
方法,并传递给它一个执行转换的函数
--------------------------------------------------------------------------------------------------------------------
List wordList=……;
Stream words=wordList.stream();
Stream lowercaseWords=words.
map
(String::toLowerCase);
Stream firstChars=words.
map
(s->s.charAt(0));
--------------------------------------------------------------------------------------------------------------------
3.flatMap
假设有一个函数,给他一个字符串,返回一个包含多个值的流,如果将该函数传递给
map()
,将会产生一个包含多个流的流
--------------------------------------------------------------------------------------------------------------------
public static Stream characterStream(String s){
List result=new ArrayList<>();
for(char c:s.toCharArray()){
result.add(c);
}
return result.stream();
}
Stream> result=words.
map
(w->characterStream(w));
//result为[...,['y','o','u'],[j'','b','k']]
--------------------------------------------------------------------------------------------------------------------
如果想要获取一个只包含字符串的流,需要使用
flatMap
方法而不是
map
方法
--------------------------------------------------------------------------------------------------------------------
public static Stream characterStream(String s){
List result=new ArrayList<>();
for(char c:s.toCharArray()){
result.add(c);
}
return result.stream();
}
Stream> result=words.
flatMap
(w->characterStream(w));
//result为[...,"y","o","u","j","b","k"]
--------------------------------------------------------------------------------------------------------------------
四、提取子流和组合流
1.
limit(n)
:返回一个包含前n个元素的新流,如果原始流的长度小于n,则会返回原始的流
--------------------------------------------------------------------------------------------------------------------
Stream randoms=
Stream
.
generate
(
Math::random
).
limit
(100);
--------------------------------------------------------------------------------------------------------------------
2.
skip(n)
:丢弃前n个元素
--------------------------------------------------------------------------------------------------------------------
Stream integers=
Stream
.
iterate
(BigInteger.ZERO
,
n->n.add
(BigInteger.ONE)).
limit
(100);
--------------------------------------------------------------------------------------------------------------------
3.
Stream
.
concat()
:将两个流连接到一起,第一个流的长度不应该是无限的
--------------------------------------------------------------------------------------------------------------------
Stream 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 uniqueWords=
Stream
.
of
("merrily","merrily","gently").
distinct()
;
--------------------------------------------------------------------------------------------------------------------
2.
sorted()
:遍历整个流,产生并返回一个新的已排序的流,Java8中提供了多个sorted方法,其中一个用于其中元素实现了Comparable接口的流,另一个接收一个Comparator对象
--------------------------------------------------------------------------------------------------------------------
Stream longestFirst=words.
sorted
(Comparator.comparing(
String::length
)
.
reversed()
);
--------------------------------------------------------------------------------------------------------------------
六、简单的聚合方法
聚合方法都是终止操作,当一个流应用了终止操作后,它就不能再应用其他的操作了
1.
count()
:返回流中元素的总数
2.
max()
、
min()
:返回流中最大值和最小值
--------------------------------------------------------------------------------------------------------------------
Optional largest=words.
max
(
String::compareToIgnoreCase
);
if(largest.
isPresent()
){
System.out.println("largest:"+largest.get());
}
--------------------------------------------------------------------------------------------------------------------
3.
findFirst()
:返回非空集合中的第一个值,通常与filter方法结合使用
--------------------------------------------------------------------------------------------------------------------
Optional startsWithQ=words.
filter
(
s->s.startsWith("Q")
).
findFirst()
;
--------------------------------------------------------------------------------------------------------------------
4.
findAny()
:返回集合中的任意一个值,并行执行时十分有效
--------------------------------------------------------------------------------------------------------------------
Optional startsWithQ=words.
parallel()
.
filter
(
s->s.startsWith("Q")
).
findAny()
;
--------------------------------------------------------------------------------------------------------------------
这些方法会返回一个
Optional
值,它可能会封装返回值,也可能标识没有返回(当流为空)Optional类型是一种更好的标识缺少返回值的方式
5.
anyMatch()
:查看流中是否有匹配元素,该方法接收一个
predicate
参数,并行执行时十分有效
--------------------------------------------------------------------------------------------------------------------
boolean b=words.
parallel()
.
anyMatch(s->s.startsWith("Q"))
;
--------------------------------------------------------------------------------------------------------------------
6.
allMatch()
、
noneMatch()
:查看流中是否所有元素匹配或没有元素匹配,仍可以通过并行执行来提高速度
七、Optional类型
Optional对象或者是对一个T类型对象的封装,或者表示不是任何对象。因为它不会返回null,所以在正确使用的前提下比一般指向T类型的引用更安全
get()
:如果存在被封装对象,则返回该对象,否则抛出NoSuchuElementException异常
isPresent()
:返回一个boolean值,反映Optional对象是否有值
1.使用Optional值
高效使用Optional的关键在于,使用一个或者接收正确值、或者返回另一个替代值的方法
(1).
ifPresent()
:接收一个函数,如果存在可选值,将该值传给函数,否则不会进行任何操作
--------------------------------------------------------------------------------------------------------------------
optionalValue.
ifPresent
(
results::add
);
optionalValue.
ifPresent
(
v->results.add(v)
);
--------------------------------------------------------------------------------------------------------------------
(2).
map()
:接收一个函数,功效与
ifPresent()
一致,但是会返回Optional
值可能为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 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
的方法f,并且目标类型T有一个会返回
Optional
的方法g,如果都是普通的方法,我们可能会考虑通过调用s.f().g()将他们组合起来,但是这种组合在这里是行不通的,因为s.f()方法返回的是
Optional
而不是T。此时可以调用
optional =s.f().
flatMap
(
T::g
);
也就是说
flatMap()
方法可以通过展开方法所返回的流,将两个方法组合起来使用
八、聚合操作
如果希望对元素求和,或者以其他方式将流中的元素组合为一个值,可以使用聚合方法
reduce()
--------------------------------------------------------------------------------------------------------------------
Stream values=……;
Optional 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方法
--------------------------------------------------------------------------------------------------------------------
HashSetresult=
stream.
collect
(
HashSet::new
,
HashSet::add
,
HashSet::addAll
);
--------------------------------------------------------------------------------------------------------------------
然而实际中并不一定需要传递这么多参数,因为
Collector
接口已经为我们提供了这三个方法并且
Collectors
类还为常用的收集类型提供了各个工厂方法,因此:
将一个流收集到一个list或者set中
--------------------------------------------------------------------------------------------------------------------
List listResult=stream.
collect
(
Collectors
.
toList()
);
Set setResult=stream.
collect
(
Collectors
.
toSet()
);
//若要控制得到的集合类型可以通过如下方式
TreeSet 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 idToName=
people.
collect
(
Collectors
.
toMap
(Person::getId,Person::getName));
--------------------------------------------------------------------------------------------------------------------
2.如果有多个元素拥有相同的键,那么收集方法会抛出一个IllegalStateException异常,可以通过第三个函数参数,根据已有的值和新值来决定键的值,从而重写该行为
--------------------------------------------------------------------------------------------------------------------
Stream locales =
Stream
.
of
(Locale.
getAvailableLocales
());
Map collect = locales.
collect
(
Collectors
.
toMap
(
l -> l.getDisplayLanguage(),
l -> l.getDisplayLanguage(l),
(existingValue, newValue) -> existingValue
));
--------------------------------------------------------------------------------------------------------------------
3.如果你需要指定Map集合的类型,需要提供一个构造函数作为第4个参数
--------------------------------------------------------------------------------------------------------------------
Map idToName=
people.
collect
(
Collectors
.
toMap
(Person::getId,Person::getName,
(existingValue,newValue)->{throw new IllegalStateException();},
TreeMap::new
));
--------------------------------------------------------------------------------------------------------------------
4.如果希望通过一个键来获取多个值,例如希望知道指定国家中的所有语言,那么就需要一个Map>对象,可以将每个国家中的各种语言存储到单独的集合中,当发现指定国家的一种新语言时,我们就将已有值和新值组合成一个新的集合
--------------------------------------------------------------------------------------------------------------------
Map> countryLanguageSets=locales.
collect
(
Collectors
.
toMap
(
l->l.getDisplayCountry(),
l->
Collections
.
singleton
(l.getDisplayLanguage()),
(a,b)->{
Set r= new HashSet<>(a);
r.addAll(b);
return r;
}
));
--------------------------------------------------------------------------------------------------------------------
5.对于toMap方法的每种形式,都有一个对应的toConcurrentMap方法,用来产生一个并发的map。再并发收集过程中应当只使用一个并发的map,当在并行流中使用并发map时,一个共享的map要比合并map效率更高,但是当然,使用共享的map无法得到有序的结果
十一、分组和分片
1.具有相同特性的值进行分组是一个很常见的任务,可以直接使用
groupingBy
方法
--------------------------------------------------------------------------------------------------------------------
Map> countryToLocales=locales.
collect
(
Collectors
.
groupingBy
(
Locale::getCountry
));
List swissLocales=countryToLocales.get("CH");
--------------------------------------------------------------------------------------------------------------------
Locale::getCountry
是进行分组的分类函数
如果调用
groupingByConcurrent
方法,会获得一个并发map,当用于并行流时可以并发的插入值。这同
toConcurrentMap
方法是完全类似的
2.当分类函数是一个predicate函数(即返回一个布尔值的函数)时,流元素会被分为两组列表:一组是函数会返回true的元素,另一组返回false的元素。
在这种情况下,使用
partitioningBy
比使用
groupingBy
更有效率
--------------------------------------------------------------------------------------------------------------------
Map> en =
locales.
collect
(
Collectors
.
partitioningBy
(l -> l.getLanguage().equals("en")));
--------------------------------------------------------------------------------------------------------------------
3.downstream收集器
(1).
Collectors
.
toSet()
如果希望指定值得类型,需要提供一个"downstream收集器",例如希望map得值
是set而不是list可以提供
Collectors
.
toSet
方法
--------------------------------------------------------------------------------------------------------------------
Map> countryToLocales=
locales.
collect
(
Collectors
.
groupingBy
(Locale::getCountry,
Collectors
.
toSet()
));
--------------------------------------------------------------------------------------------------------------------
(2).
Collectors
.
counting()
:返回所收集元素的总个数
--------------------------------------------------------------------------------------------------------------------
Map collect =
locales.
collect
(
Collectors
.
groupingBy
(Locale::getCountry,
Collectors
.
counting()
));
--------------------------------------------------------------------------------------------------------------------
(3).
Collectors
.
summing
(Int|Long|Double)
该方法接受一个函数作为参数参数,它会将该函数应用到downstream元素中,并生成它们的求和
--------------------------------------------------------------------------------------------------------------------
Map stateToCityPopulation=
cities.
collect
(
Collectors.
groupingBy
(City::getState,
Collectors
.
summingInt
(City::getPopulation)));
--------------------------------------------------------------------------------------------------------------------
(4).
Collectors
.
maxBy()
|
minBy()
该方法接受一个比较器,并声称downstream元素中的最大值和最小值
--------------------------------------------------------------------------------------------------------------------
Map stateToCityPopulation=
cities.
collect
(
Collectors.
groupingBy
(City::getState,
Collectors
.
maxBy
(
Comparator
.
comparing
(City::getPopulation))));
--------------------------------------------------------------------------------------------------------------------
(5).
Collectors
.
mapping()
该方法将一个函数应用到downstream结果上,并且需要另一个收集器来处理结
果
--------------------------------------------------------------------------------------------------------------------
Map stateToCityPopulation=
cities.
collect
(
Collectors.
groupingBy
(City::getState,
Collectors
.
mapping
(City::getName,
Collectors
.
maxBy
(
Comparator
.
comparing
(String::length))));
--------------------------------------------------------------------------------------------------------------------
在10.4节中,获取指定国家所有语言集合时,我们用了toMap方法,这里可以给
出一个更好的解决方案
--------------------------------------------------------------------------------------------------------------------
Map> collect =
locales.
collect
(
Collectors
.
groupingBy
(l -> l.getDisplayCountry(),
Collectors
.
mapping
(l -> l.getDisplayLanguage(),
Collectors
.
toSet()
)));
--------------------------------------------------------------------------------------------------------------------
(6).如果grouping或者mapping函数的返回类型是int、long或者double,你可以将元素收集到一个summary statics对象中
--------------------------------------------------------------------------------------------------------------------
Map 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 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
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结果的方法会返回一个OptionalInt、OptionalLong或者OptionalDouble类型,这些类与Optional类类似,但是没有get方法,而使用对应的getAsInt()、getAsLong()、getAsDouble()来代替
- 方法sum、average、max和min会返回总和、平均值、最大值和最小值,而对象流中没有这些方法
- summaryStatistics方法会产生一个IntSummaryStatistics、LongSummaryStatistics或者DoubleSummaryStatistics对象
7.Java8中,
Random
类提供了
ints
、
longs
和
doubles
方法,用来返回包含随机数字的原始类型流
十三、函数式接口
1.有时在查看javadoc文档时,会看到类似:
Stream filter(
Predicate
super T> predicate)
Predicate
是一个接口,只含有一个返回boolean值的非默认方法,一般只需要记
住Predicate是一个返回boolean值的函数就行了
--------------------------------------------------------------------------------------------------------------------
public interface Predicate{
boolean test(T argument);
}
--------------------------------------------------------------------------------------------------------------------
2.在Stream API中常见的函数式接口
接口
|
参数类型
|
返回类型
|
描述
|
Supplier
|
无
|
T
|
提供一个T类型的值
|
Consumer
|
T
|
void
|
处理一个T类型的值
|
BiConsumer
|
T,U
|
void
|
处理T类型和U类型的值
|
Predicate
|
T
|
boolean
|
计算boolean值的函数
|
ToIntFunction
ToLongFunction
ToDoubleFunction
|
T
|
int
long
double
|
分别计算int、long、
double值的函数
|
IntFunction
LongFunction
DoubleFunction
|
int
long
double
|
R
|
参数分别为int、long或
double类型的函数
|
Function
|
T
|
R
|
一个参数为类型为T的
函数
|
BiFunction
|
T,U
|
R
|
一个参数类型为T和U的
函数
|
UnaryOperator
|
T
|
T
|
对类型T进行的一元操作
|
BinaryOperator
|
T,T
|
T
|
对类型T进行的二元操作
|