从迭代到流的操作
- 流表面上看起来和集合很类似,都可以让我们转换和获取数据,但是它们之间存在着显著的差异
- 流并不存储其元素,这些元素可能存储在底层的集合中,或者是按需生成的
- 流的操作不会修改其数据源
- 流的操作是尽可能性执行的,这意味着直至需要其结果时,操作才会执行
- 工作流的典型流程
- 创建一个流
- 指定将初始流转换为其他流的中间操作,可能包含多个步骤
- 应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作,从此之后,这个流就再也不能用了
流的创建
Collection
接口的stream()
方法:可以将任何一个集合转化成流Stream.of()
- 作用:可以将数组转化成流
- 参数:具有可变长参数,所以可以使用其构建具有任意数量引元的流
Arrays.stream(array, from, to)
:可以从数组中于 from 和 to (不包括) 的元素中创建一个流Stream.empty()
:创建不包含任何元素的流Stream.generate()
:会接受一个不包含任何引元的函数 (从技术上讲,是一个 Supplier <T> 接口的对象)。无论何时,只要需要一个流类型的值,该函数就会被调用以产生一个这样的值Stream.iterate()
:接受一个 "种子" 值,以及一个函数 (从技术上讲,是一个 UnaryOperation <T>),并且会反复地将该函数应用到之前的结果上
// Collection接口List<String> words = new ArrayList<String>();Stream<String> streamWords = words.stream();// Stream.of()String[] words = {"123", "456", "789"};Stream<String> streamWords = Stream.of(words);// Array.stream()Stream<String> streamWords = Array.stream(words, 0, 3);// Stream.generate()// 产生一个随机数流Stream<Double> randomDouble = Stream.generate(Math::random);// Stream.iterate()// 产生一个 0,1,2,... 无限序列Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger>ONE));
filter、map 和 flatMap 方法
filter()
方法- 作用:其会转换产生一个流,其中的元素与某种条件相匹配
- 参数:
filter()
的引元是Predicate<T>
,即从 T 到 boolean 的函数
map()
方法:将传入的函数应用到每个元素上从而产生新的流flatMap()
方法:假设我们有一个泛型 G,以及将某种类型T转换为 G<U> 的函数 f 和将类型 U 转换为 G<V> 的函数 g,我们可以通过使用 flatMap 来组合它们,即首先应用 f,然后应用 g
// 使用 filter() 获取大于12个字母的单词Stream<String> bigWords = words.stream().filter(w -> w.length() > 12);// 使用 map() 将单词全部转为小写Stream<String> lowerCaseWords = words.stream().map(String::toLowerCase);// 使用 flatMap() 将一个字流中流摊平为一个流// 获得形如 [["y", "o", "u", "r"],["a", "r", "e"]] 这样的流中流Stream<Stream<String>> result = words.stream().map(w -> letters(w));// flatMap() 可将其摊平,获得形如 ["y", "o", "u", "r", "a", "r", "e"] 这样的Stream<String> flatResult = words.stream().flatMap(w -> letters(w));
抽取子流和连接流
limit(n)
:将原流的前$n$个元素组成一个新的流返回skip(n)
:返回丢弃原流的前$n$个元素形成的新流Stream.concat()
- 作用:将两个流拼接起来
- 注意:若第一个流是无限的,则第二个流将永远得不到处理
其他的转换流
distinct()
:返回一个由原流中剔除重复元素后所有的元素组成的新流sort()
- 直接对 Comparable 元素的流进行排序操作
- 接受一个 Comparator
peek()
:其元素与原流相同,但是每回获取一个元素时都会调用作为其参数的函数
ArrayList<String> words; // "ccc", "a", "bb"// sort() 直接对 Comparable 元素的流进行排序操作words.stream().sort(); // "a", "bb", "ccc"// sort() 接受一个 Comparator 作为参数words.stream().sorted(Comparator.comparing(String::length).reverse()); // "ccc", "bb", "a"
简单约简
- 约简是一种终结操作,它们会将流约简为可以在程序中使用的非流值
- 常见简约操作
long count()
:返回流中元素个数boolean anyMatch(Predicate<? super T> predicate)
:在这个流中任意元素匹配给定断言时返回 trueboolean allMatch(Predicate<? super T> predicate)
:在这个流中所有元素匹配给定断言时返回 trueboolean noneMatch(Predicate<? super T> predicate)
:在这个流中没有任何元素匹配给定断言时返回 trueOptional<T> max(Comparator<? super T> comparator)
:产生这个流的最大元素,使用由给定比较器定义的排序规则,如果这个流为空,会产生一个空的 Optional 对象Optional<T> min(Comparator<? super T> comparator)
:产生这个流的最小元素,使用由给定比较器定义的排序规则,如果这个流为空,会产生一个空的 Optional 对象Optional<T> findFirst()
:产生这个流的第一个元素,如果这个流为空,会产生一个空的 Optional 对象Optional<T> findAny()
:产生这个流的任意一个元素,如果这个流为空,会产生一个空的 Optional 对象
Optional 类型
- 定义
Optional<T>
对象是一种包装器对象,要么包装了类型 T 的对象,要么没有包装任何对象- 对于上述的第一种情况,我们成为这种值是存在的
- 作用:
Optional<T>
类型被当作一种更安全的方式,用来替代类型 T 的引用,这种引用要么引用某个对象,要么为 null
如何使用 Optional 值
- 有效使用 Optional 值的关键:它在值不存在的情况下会产生一个可替代物,而只有在值存在的情况下才会使用这个值
- 常用使用方法
T orElse(T other)
:产生这个 Optional 的值,或者在该 Optional 为空时,产生 otherT orElseGet(Supplier<? extends T> other)
:产生这个 Optional 的值,或者在该为空时,产生调用 other 的结果<X extends Throwable> orElseThrow(Supplier<? extends X> exceptionSupplier)
:产生这个 Optional 的值,或者在该 Optional 为空时,抛出调用 exceptionSupplier 的结果void ifPresent(Consumer<? extneds T> consumer)
:如果该 Optional 不为空,那么就将它的值传递给 consumer<U> Optional<U> map(Function<? super T, ? extneds U> mapper)
:只要这个 Optional 不为空且结果不为 null,将产生该 Optional 的值传递给 mapper 后的结果,否则产生一个空 Optional
// ifPresent():如果 optionalValue 可选值存在,则将其加入到 res 中optionalValue.ifPresent(v -> res.add(v));// map()// added 具有三种值之一:在 optionalValue 存在的情况下包装在 Optional 中的 true 或 false,以及在 optionalValue 不存在的情况下的空 OptionalOptional<Boolean> added = optionalValue.map(res::add);
不适合使用 Optional 值的方式
get()
:会在 Optional 值存在的情况下获得其中包装的元素,或者在不存在的情况下抛出一个 NoSuchElementException 对象isPresent()
:如果 Optional 不为空,则返回 true- 注意:使用上述这两种方法并不比直接使用包装在其中的元素简便或者安全
创建 Optional 值
Optional.of(value)
:创建一个 Optional 值;如果 value 为 null,其会抛出一个 NullPointerException 对象Optional.ofNullable(value)
:创建一个 Optional 值;如果 value 为 null,其会产生一个空 OptionalOptional.empty()
:产生一个空 Optional
使用 flatMap 来构建 Optional 的值
- 使用场景:假设你有一个可以产生 Optional<T> 对象的方法 f,并且目标类型具有一个可以产生 Optional<T> 对象的方法 g。如果需要将两种方法结合起来,即将一种的结果作为另一种的参数,则需要使用 flatMap 才能正常结合
- 返回值:如果
s.f()
的值存在,那么 g 就可以应用到它上面,否则就会返回一个空 Optional
收集结果
iterator()
:产生可用于访问元素的旧式风格迭代器forEach()
- 作用:将某个函数应用于所有元素
- 注意:当应用在并行流上时,forEach 方法会以任意顺序遍历各个元素
forEachOrdered()
:按照流中的顺序遍历各个元素,但是这个方法会丧失并行处理的部分甚至全部toArray()
- 因为无法在运行时创建泛型数组,所以其会返回个 Object[]
- 如果想要让数组具有正确的类型,可以将其传递到数组构造器中
collect()
:其接受一个 Collector 接口的实例,可以将流中的元素收集到另一个目标中。Collectors 类提供了大量用于生成公共收集器的工厂方法- 收集到列表中:
stream.collect(Collectors.toList())
- 收集到集合中:
stream.collect(Collectors.toSet())
- 收集到特定的数据结构中:
stream.collect(Collectors.toCollection(CertainDataStructure::new))
- 通过连接操作收集流中所有字符串:
stream.collect(Collectors.joining(str))
,其中 str 是分隔符,不是必要的参数 - 流中含有除字符串以外的对象:
stream.map(Object::toString).collect(Collectors.joining())
- 收集到列表中:
Collectors.summarizingInt\Long\Double
:如果想要将流的结果约简为总和、平均值、最大值或最小值,可以使用 summarizingInt\Long\Double 方法中的某一个,这些方法会接受一个将流对象映射为数据的函数同时,这些方法会产生类型为 Int\Long\DoubleSummaryStatistics 的结果,同时计算总和、数量、平均值、最小值和最大值
// summarizingIntIntSummaryStatistics statistics = stream.collect(Collectors.summarizingInt(String::length));statistics.getSum();statistics.getCount();statistics.getAverage();statistics.getMax();statistics.getMin();
收集到映射表中
- 方法:
Collectors.toMap()
- 实用方法
// 两个参数:用来产生映射表的键和值// 如果多个元素具有相同的键,收集器会抛出一个 IllegalStateexception 对象Map<Integer, String> idToName = stream.collect(Collectors.toMap(Person::getId, Person::getName));Map<Integer, String> idToPerson = stream.collect(Collectors.toMap(Person::getId, Function.identity()));// 第三个参数:该函数会针对给定的已有值和新值来解决冲突并确定键对应的值Map<Integer, String> idToPerson = stream.collect(Collectors.toMap(Person::getId, Function.identity(), (existValue, newValue) -> existValue));// 第三个参数:将构造器作为参数传入Map<Integer, String> idToPerson = stream.collect(Collectors.toMap(Person::getId, Function.identity(), (existValue, newValue) -> existValue),TreeMap::new);
群组和分区
Collectors.groupingBy()
:将具有相同的值群聚成组Collectors.partitioningBy()
:当分类函数是返回 boolean 的函数时,使用这个函数更加高效,其会产生一个键为 true/false 的映射表
// 将具有相同 id 的 Person 对象分为一类Map<Integer, List<Person>> idToManyNames = stream.collect(Collectors.groupingBy(Person::getId));// 分类函数为 boolean 函数时Map<Boolean, List<Person>> idEqualsCertainId = stream.collect(Collectors.partitioningBy( person -> person.id == 1));
下游收集器
- 作用:如果要以某些方式来处理由 groupBy 得到的列表,就需要提供一个 "下游收集器"
- 使用方法:将方法作为 groupBy 的第二个参数
- 常用下游收集器
Collectors.toSet()
:获得集合而不是列表Collectors.counting()
:产生收集到的元素的个数Collectors.summing()
:接受一个函数作为引元,将该函数应用到下游元素中,并产生它们的和Collectors.maxBy()
:接受一个比较器,并产生下游元素中的最大值Collectors.minBy()
:接受一个比较器,并产生下游元素中的最小值Collectors.mapping()
:产生将函数应用到下游结果上的收集器,并将函数值传递给另一个收集器Collectors.summarizingInt\Long\Double()
:获取 Int\Long\DoubleSummaryStatistics
// mapping()// 获取一个州内具有最长名字的城市的名字Map<String, Optional<String>> longestCityInState = cities.collect( groupingBy(City::getState, mapping(City::getName, maxBy(Comparator.comparing()))));
约简操作
- 方法:
reduce()
- 作用:从流中计算某个值的通用机制
- 几种使用方法
- 一个参数
- 接受一个二元函数,并从前两个元素开始持续应用它
- 这个操作必须是可结合的
- 两个参数:第一个参数为这个操作的幺元,第二个参数为上述的二元函数
- 三个参数:第一个参数为这个操作的幺元;第二个参数为上述的二元函数;第三个参数为一个结合函数,用于结合并行计算时产生的多个结果
- 一个参数
// 三个参数// 统计流中单词的长度int totalLength = stream.reduce(0, (total, word) -> total + word.length(), (total1, total2) -> total1 + total2);
基本类型流
- 三种基本类型流:IntStream、LongStream、DoubleStream
- 创建基本类型流
Int\Long|DoubleStream.of()
Arrays.stream()
- 生成步长为$1$的整数流
Int\LongStream.range(a, b)
:不包括 bInt\LongStream.rangeClosed(a, b)
:包括 b
- CharSequence 接口中的两种方法
codePoints
:生成由字符的 Unicode 码构成的 IntStreamchars
:生成由 UTF-16 编码机制的码元构成的 IntStream
- 对象流转换成基本类型流:
mapToInt\Long\Double()
- 装箱成对象流:
boxed()
- 基本类型流与对象流在方法上的差异
- toArray 方法会返回基本类型数组
- 产生可选结果的方法会返回一个 OptionalInt、 OptionalLong 或 OptionaIDouble,这些类与 Optional 类似,但是具有 getAsInt、getAsLong 和 getAsDouble 方法,而不是 get 方法
- 具有返回总和、平均值、最大值和最小值的 sum、average、max 和 min 方法,对象流没有定义这些方法
- summaryStatistics 方法会产生一个类型为 IntSummaryStatistics、LongSummaryStatistics 或 DoubleSummaryStatistics 的对象
并行流
- 获取并行流
parallelStream()
:从集合中直接获取一个并行流parallel()
:将一个流转化为并行流
- 放弃排序需求:当放弃排序需求时,有些操作可以更有效的并行化,通过再流上调用
unordered()
方法,就可以明确的表示对排序不感兴趣 - 注意
- 不要修改在执行某项流操作后会将元素返回到流中的集合 (即使这种修改是线程安全的)
- 数据应该在内存中,必须等到数据到达是非常低效的
- 流应该可以被高效地分成若干个子部分,由数组或平衡二叉树支撑的流都可以工作得很好,但是 Stream. iterate 返回的结果不行
- 流操作的工作量应该具有较大的规模,如果总工作负载并不是很大,那么搭建并行计算时所付出的代价就没有什么意义
- 传递给并行流操作的函数不应该被堵塞,并行流使用 fork-join 池来操作流的各个部分,如果多个流操作被阻塞,那么池可能就无法做任何事情了
原文转载:http://www.shaoqun.com/a/605607.html
net a porter:https://www.ikjzd.com/w/2132
优1宝贝:https://www.ikjzd.com/w/1507
从迭代到流的操作流表面上看起来和集合很类似,都可以让我们转换和获取数据,但是它们之间存在着显著的差异流并不存储其元素,这些元素可能存储在底层的集合中,或者是按需生成的流的操作不会修改其数据源流的操作是尽可能性执行的,这意味着直至需要其结果时,操作才会执行工作流的典型流程创建一个流指定将初始流转换为其他流的中间操作,可能包含多个步骤应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作,从此之
家得宝:https://www.ikjzd.com/w/1570
萌店:https://www.ikjzd.com/w/1538
海维:https://www.ikjzd.com/w/1891
2019年1月1日起,德国或将开始征收销售税!:https://www.ikjzd.com/home/3903
欧代是什么?Amazon亚马逊卖家欧代注册流程,费用和时效详细解。:https://www.ikjzd.com/home/132135
跨境电商周报:安克创新前三季度营收增超32%:https://www.ikjzd.com/home/132772
没有评论:
发表评论