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,该聚合会产生v0opv1opv2op……,其中viopvi+1就表示我们编写的函数调用op(vi,vi+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结果的方法会返回一个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<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进行的二元操作 |