郑州网站开发工程师,中国八大设计院排名,如何注册国外域名,电子商务网站建设与管理课后题答案6文章目录 一、创建流二、中间操作和收集操作筛选 filter去重distinct截取跳过映射合并多个流是否匹配任一元素#xff1a;anyMatch是否匹配所有元素#xff1a;allMatch是否未匹配所有元素#xff1a;noneMatch获取任一元素findAny获取第一个元素findFirst归约数值流的使用中… 文章目录 一、创建流二、中间操作和收集操作筛选 filter去重distinct截取跳过映射合并多个流是否匹配任一元素anyMatch是否匹配所有元素allMatch是否未匹配所有元素noneMatch获取任一元素findAny获取第一个元素findFirst归约数值流的使用中间操作和收集操作总结Collector 收集归约一般性归约汇总分组多级分组转换类型数据分区并行流 stream在使用中主要分为以上图示阶段接下来将详细展示java8中stream api的具体的实践使用案例。 一、创建流
在使用流之前首先需要拥有一个数据源并通过StreamAPI提供的一些方法获取该数据源的流对象。数据源可以有多种形式
1. 集合
这种数据源较为常用通过stream()方法即可获取流对象
ListPerson list new ArrayListPerson();
StreamPerson stream list.stream();2. 数组
通过Arrays类提供的静态函数stream()获取数组的流对象
String[] names {chaimm,peter,john};
StreamString stream Arrays.stream(names);3. 值
直接将几个值变成流对象
StreamString stream Stream.of(chaimm,peter,john);4. 文件
try(Stream lines Files.lines(Paths.get(“文件路径名”),Charset.defaultCharset())){//可对lines做一些操作
}catch(IOException e){
}5. iterator
创建无限流
Stream.iterate(0, n - n 2).limit(10).forEach(System.out::println);PSJava7简化了IO操作把打开IO操作放在try后的括号中即可省略关闭IO的代码。 二、中间操作和收集操作
筛选 filter
filter 函数接收一个Lambda表达式作为参数该表达式返回boolean在执行过程中流将元素逐一输送给filter并筛选出执行结果为true的元素。 如筛选出所有学生
ListPerson result list.stream().filter(Person::isStudent).collect(toList());去重distinct
去掉重复的结果
ListPerson result list.stream().distinct().collect(toList());截取
截取流的前N个元素
ListPerson result list.stream().limit(3).collect(toList());跳过
跳过流的前n个元素
ListPerson result list.stream().skip(3).collect(toList());映射
对流中的每个元素执行一个函数使得元素转换成另一种类型输出。流会将每一个元素输送给map函数并执行map中的Lambda表达式最后将执行结果存入一个新的流中。 如获取每个人的姓名(实则是将Perosn类型转换成String类型)
ListPerson result list.stream().map(Person::getName).collect(toList());合并多个流
例列出List中各不相同的单词List集合如下
ListString list new ArrayListString();
list.add(I am a boy);
list.add(I love the girl);
list.add(But the girl loves another girl);思路如下
首先将list变成流
list.stream();按空格分词
list.stream().map(line-line.split( ));分完词之后每个元素变成了一个String[]数组。
将每个 String[] 变成流
list.stream().map(line-line.split( )).map(Arrays::stream)此时一个大流里面包含了一个个小流我们需要将这些小流合并成一个流。
将小流合并成一个大流用 flatMap 替换刚才的 map
list.stream().map(line-line.split( )).flatMap(Arrays::stream)去重
list.stream().map(line-line.split( )).flatMap(Arrays::stream).distinct().collect(toList());是否匹配任一元素anyMatch
anyMatch用于判断流中是否存在至少一个元素满足指定的条件这个判断条件通过Lambda表达式传递给anyMatch执行结果为boolean类型。 如判断list中是否有学生
boolean result list.stream().anyMatch(Person::isStudent);是否匹配所有元素allMatch
allMatch用于判断流中的所有元素是否都满足指定条件这个判断条件通过Lambda表达式传递给anyMatch执行结果为boolean类型。 如判断是否所有人都是学生
boolean result list.stream().allMatch(Person::isStudent);是否未匹配所有元素noneMatch
noneMatch与allMatch恰恰相反它用于判断流中的所有元素是否都不满足指定条件
boolean result list.stream().noneMatch(Person::isStudent);获取任一元素findAny
findAny能够从流中随便选一个元素出来它返回一个Optional类型的元素。
OptionalPerson person list.stream().findAny();获取第一个元素findFirst
OptionalPerson person list.stream().findFirst();归约
归约是将集合中的所有元素经过指定运算折叠成一个元素输出如求最值、平均数等这些操作都是将一个集合的元素折叠成一个元素输出。
在流中reduce函数能实现归约。 reduce函数接收两个参数
初始值进行归约操作的Lambda表达式
元素求和自定义Lambda表达式实现求和
例计算所有人的年龄总和
Test
public void contextLoads() {ListPerson list new ArrayList();list.add(new Person().setAge(20));list.add(new Person().setAge(25));int age list.stream().map(Person::getAge).reduce(0, Integer::sum);System.out.println(age);
}
Data
Accessors(chain true)
class Person {private int age;
}reduce的第一个参数表示初试值为0reduce的第二个参数为需要进行的归约操作它接收一个拥有两个参数的Lambda表达式reduce会把流中的元素两两输给Lambda表达式最后将计算出累加之和。
元素求和使用Integer.sum函数求和
上面的方法中我们自己定义了Lambda表达式实现求和运算如果当前流的元素为数值类型那么可以使用Integer提供了sum函数代替自定义的Lambda表达式如
int age list.stream().reduce(0, Integer::sum);Integer类还提供了 min、max 等一系列数值操作当流中元素为数值类型时可以直接使用。
数值流的使用
采用reduce进行数值操作会涉及到基本数值类型和引用数值类型之间的装箱、拆箱操作因此效率较低。 当流操作为纯数值操作时使用数值流能获得较高的效率。
将普通流转换成数值流
StreamAPI提供了三种数值流IntStream、DoubleStream、LongStream也提供了将普通流转换成数值流的三种方法mapToInt、mapToDouble、mapToLong。 如将Person中的age转换成数值流
IntStream stream list.stream().mapToInt(Person::getAge);数值计算
每种数值流都提供了数值计算函数如max、min、sum等。如找出最大的年龄
OptionalInt maxAge list.stream().mapToInt(Person::getAge).max();由于数值流可能为空并且给空的数值流计算最大值是没有意义的因此max函数返回OptionalInt它是Optional的一个子类能够判断流是否为空并对流为空的情况作相应的处理。 此外mapToInt、mapToDouble、mapToLong进行数值操作后的返回结果分别为OptionalInt、OptionalDouble、OptionalLong
中间操作和收集操作总结
操作类型返回类型使用的类型/函数式接口函数描述符filter中间StreamTPredicateTT - booleandistinct中间StreamTskip中间StreamTlongmap中间StreamRFunctionT, RT - RflatMap中间StreamRFunctionT, StreamRT - StreamRlimit中间StreamTlongsorted中间StreamTComparatorT(T, T) - intanyMatch终端booleanPredicateTT - booleannoneMatch终端booleanPredicateTT - booleanallMatch终端booleanPredicateTT - booleanfindAny终端OptionalTfindFirst终端OptionalTforEach终端voidConsumerTT - voidcollect终端RCollectorT, A, Rreduce终端OptionalTBinaryOperatorT(T, T) - Tcount终端long
Collector 收集
收集器用来将经过筛选、映射的流进行最后的整理可以使得最后的结果以不同的形式展现。 collect 方法即为收集器它接收 Collector 接口的实现作为具体收集器的收集方法。 Collector 接口提供了很多默认实现的方法我们可以直接使用它们格式化流的结果也可以自定义 Collector 接口的实现从而定制自己的收集器。
归约
流由一个个元素组成归约就是将一个个元素“折叠”成一个值如求和、求最值、求平均值都是归约操作。
一般性归约
若你需要自定义一个归约操作那么需要使用 Collectors.reducing 函数该函数接收三个参数
第一个参数为归约的初始值第二个参数为归约操作进行的字段第三个参数为归约操作的过程
汇总
Collectors类专门为汇总提供了一个工厂方法Collectors.summingInt。 它可接受一 个把对象映射为求和所需int的函数并返回一个收集器该收集器在传递给普通的 collect 方法后即执行我们需要的汇总操作。
分组
数据分组是一种更自然的分割数据操作分组就是将流中的元素按照指定类别进行划分类似于SQL语句中的 GROUPBY。
多级分组
多级分组可以支持在完成一次分组后分别对每个小组再进行分组。 使用具有两个参数的 groupingBy 重载方法即可实现多级分组。
第一个参数一级分组的条件第二个参数一个新的 groupingBy 函数该函数包含二级分组的条件
Collectors 类的静态工厂方法
工厂方法返回类型用途示例toListListT把流中所有项目收集到一个 ListListProject projects projectStream.collect(toList());toSetSetT把流中所有项目收集到一个 Set删除重复项SetProject projects projectStream.collect(toSet());toCollectionCollectionT把流中所有项目收集到给定的供应源创建的集合CollectionProject projects projectStream.collect(toCollection(), ArrayList::new);countingLong计算流中元素的个数long howManyProjects projectStream.collect(counting());summingIntInteger对流中项目的一个整数属性求和int totalStars projectStream.collect(summingInt(Project::getStars));averagingIntDouble计算流中项目 Integer 属性的平均值double avgStars projectStream.collect(averagingInt(Project::getStars));summarizingIntIntSummaryStatistics收集关于流中项目 Integer 属性的统计值例如最大、最小、 总和与平均值IntSummaryStatistics projectStatistics projectStream.collect(summarizingInt(Project::getStars));joiningString连接对流中每个项目调用 toString 方法所生成的字符串String shortProject projectStream.map(Project::getName).collect(joining(, ));maxByOptionalT按照给定比较器选出的最大元素的 Optional 或如果流为空则为 Optional.empty()OptionalProject fattest projectStream.collect(maxBy(comparingInt(Project::getStars)));minByOptionalT按照给定比较器选出的最小元素的 Optional 或如果流为空则为 Optional.empty()OptionalProject fattest projectStream.collect(minBy(comparingInt(Project::getStars)));reducing归约操作产生的类型从一个作为累加器的初始值开始利用 BinaryOperator 与流中的元素逐个结合从而将流归约为单个值int totalStars projectStream.collect(reducing(0, Project::getStars, Integer::sum));collectingAndThen转换函数返回的类型包含另一个收集器对其结果应用转换函数int howManyProjects projectStream.collect(collectingAndThen(toList(), List::size));groupingByMapK, ListT根据项目的一个属性的值对流中的项目作问组并将属性值作 为结果 Map 的键MapString,ListProject projectByLanguage projectStream.collect(groupingBy(Project::getLanguage));partitioningByMapBoolean,ListT根据对流中每个项目应用断言的结果来对项目进行分区MapBoolean,ListProject vegetarianDishes projectStream.collect(partitioningBy(Project::isVegetarian));
转换类型
有一些收集器可以生成其他集合。比如前面已经见过的 toList生成了 java.util.List 类的实例。 还有 toSet 和 toCollection分别生成 Set 和 Collection 类的实例。 到目前为止 我已经讲了很多流上的链式操作但总有一些时候需要最终生成一个集合——比如
已有代码是为集合编写的因此需要将流转换成集合传入在集合上进行一系列链式操作后最终希望生成一个值写单元测试时需要对某个具体的集合做断言。
使用 toCollection用定制的集合收集元素
stream.collect(toCollection(TreeSet::new));还可以利用收集器让流生成一个值。 maxBy 和 minBy 允许用户按某种特定的顺序生成一个值。
数据分区
分区是分组的特殊情况由一个断言返回一个布尔值的函数作为分类函数它称分区函数。 分区函数返回一个布尔值这意味着得到的分组 Map 的键类型是 Boolean于是它最多可以分为两组: true是一组false是一组。
分区的好处在于保留了分区函数返回true或false的两套流元素列表。
并行流
并行流就是一个把内容分成多个数据块并用不不同的线程分别处理每个数据块的流。最后合并每个数据块的计算结果。
将一个顺序执行的流转变成一个并发的流只要调用 parallel() 方法
public static long parallelSum(long n){return Stream.iterate(1L, i - i 1).limit(n).parallel().reduce(0L,Long::sum);
}将一个并发流转成顺序的流只要调用 sequential() 方法
stream.parallel().filter(...).sequential().map(...).parallel().reduce();这两个方法可以多次调用只有最后一个调用决定这个流是顺序的还是并发的。
并发流使用的默认线程数等于你机器的处理器核心数。
通过这个方法可以修改这个值这是全局属性。
System.setProperty(java.util.concurrent.ForkJoinPool.common.parallelism, 12);并非使用多线程并行流处理数据的性能一定高于单线程顺序流的性能因为性能受到多种因素的影响。 如何高效使用并发流的一些建议
如果不确定 就自己测试。尽量使用基本类型的流 IntStream, LongStream, DoubleStream有些操作使用并发流的性能会比顺序流的性能更差比如limitfindFirst依赖元素顺序的操作在并发流中是极其消耗性能的。findAny的性能就会好很多应为不依赖顺序。考虑流中计算的性能(Q)和操作的性能(N)的对比, Q表示单个处理所需的时间N表示需要处理的数量如果Q的值越大, 使用并发流的性能就会越高。数据量不大时使用并发流性能得不到提升。考虑数据结构并发流需要对数据进行分解不同的数据结构被分解的性能时不一样的。
流的数据源和可分解性
源可分解性ArrayList非常好LinkedList差IntStream.range非常好Stream.iterate差HashSet好TreeSet好
流的特性以及中间操作对流的修改都会对数据对分解性能造成影响。 比如固定大小的流在任务分解的时候就可以平均分配但是如果有filter操作那么流就不能预先知道在这个操作后还会剩余多少元素。
考虑终端操作的性能如果终端操作在合并并发流的计算结果时的性能消耗太大那么使用并发流提升的性能就会得不偿失。