Java核心技术卷2 高级特性 学习笔记(1)

2019-02-25 14:57:49 浏览数 (1)

参考:Java核心技术卷2 高级特性 第一章

Java SE 8的流库

流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图。通过使用流,我们可以说明想要完成什么任务,而不是说明如何去实现它。将操作的调度留给具体实现去做。

流遵循了做什么而非怎么做的原则。在流的示例中,我们描述了需要做什么,没有指定该操作应该以什么顺序或者在哪个线程中执行。

流和集合的差异:

  • 流并不存储元素,这些元素可能存储在底层的集合中,或者是按需生成的
  • 流的操作不会修改其数据源,例如,filter方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素
  • 流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。
代码语言:javascript复制
// 产生一个元素为给定值的流
static <T> Stream<T> of(T… values)


// 产生一个不包含任何元素的流
static <T> Stream<T> empty()

// 产生一个无限流,它的值是通过反复调用函数s而构建的
static <T> Stream<T> generate(Supplier<T> s)


//产生一个无限流,它的元素包含种子、在种子上调用f产生的值、在前一个元素上调用f产生的值
static <T> Stream <T> iterate(T seed,UnaryOperator<T> f)

filter转换会产生一个流,它的元素与某种条件相匹配。filter的引元是Predicate<T>,即从T到boolean的函数。此时,可以使用map方法并传递执行该转换的函数。

代码语言:javascript复制
// 产生一个流,它包含当前流中所有满足断言条件的元素
Stream<T> filter(Predicate<? super T> predicate)

//产生一个流,它包含将mapper应用于当前流中所有元素所产生的结果
<R> Stream<R> map(Function<? super T, ? extends R> mapper)

//产生一个流,它是通过将mapper应用于当前流中所有元素所产生的结果连接到一起而获得的
<R> Stream<R> flatMap(Function<? super T, ?extends Stream<? extends R>> mapper)

调用stream.limit(n)会返回一个新的流,它在n个元素之后结束(如果原来的流更短,那么就会在流结束时结束)。这个方法对于裁剪无限流的尺寸会显得特别有用。

stream.skip(n)正好相反:它会丢弃前n个元素。这个方法在将文本分隔为单词时会显得很方便。

代码语言:javascript复制
// 产生一个流,其中包含了当前流中最初的maxSize个元素
Stream<T> limit(long maxSize)

//产生一个流,它的元素是当前流中除了前n个元素之外的所有元素
Stream<T> skip(long n)

//产生一个流,它的元素是a的元素后面跟着b的元素
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

distinct方法会返回一个流,它的元素是从原有流中产生的,即原来的元素按照同样的顺序提出重复元素后产生的。

流的排序可以有多种sorted方法的变体可用。其中一种用于操作Comparable元素的流,而另一种可以接受一个Comparator。

peek方法会产生另一个流,它的元素与原来流中的元素相同,但是每次获取一个元素时,都会调用一个函数。

代码语言:javascript复制
// 产生一个流,包含当前流中所有不同的元素
Stream<T> distinct()

// 产生一个流,它的元素是当前流中所有元素按照顺序排列的,第一个方法要求元素是实现了Comparable的类实例
Stream<T> sorted()
Stream<T> sorted(Comparator<? super T> comparator)

// 产生一个流,它与当前流中的元素相同,在获取其中每个元素时,会将其传递给action
Stream<T> peek(Consumer<? super T> action)

简单约简包括了count、max和min。max和min返回的是一个类型Optional<T>的值,要么在其中包装了答案,要么表示没有任何值(因为流碰巧为空)。

findFirst返回的是非空集合中的第一个值,通常会在与filter组合使用时显得很有用。

如果不强调使用第一个匹配,而是使用任意的匹配都可以,那么就可以使用findAny方法。这个方法在并行流处理时会很有效,因为流可以报告任何它找到的匹配而不是被限制为必须报告第一个匹配。

如果只想知道是否存在匹配,那么可以使用anyMatch。这个方法会接受一个断言引元,因此不需要使用filter。

还有allMatch和noneMatch方法,分别会在所有元素和没有任何元素匹配断言的情况下返回true。

代码语言:javascript复制
// 分别产生这个流的最大元素和最小元素,使用由给定比较器定义的排序规则,如果这个流为空,会产生一个空的Optional对象,这些操作都是终结操作
Optional<T> max(Comparator<? super T> comparator)
Optional<T> min(Comparator<? super T> comparator)

// 分别产生这个流的第一个和任意一个元素,如果这个流为空,会产生一个空的Optional对象。这些操作都是终结操作
Optional<T> findFirst()
Optional<T> findAny()

//分别在这个流中任意元素、所有元素和没有任何元素匹配给定断言时返回true。这些操作都是终结操作。
boolean anyMatch(Predicate<? super T> predicate)
boolean allMatch(Predicate<? super T> predicate)
boolean noneMatch(Predicate<? super T> predicate)

Optional<T>对象是一种包装器对象,要么包装了类型T的对象,要么没有包装任何对象。对于第一种情况,成这种值为存在的。Optional<T>类型被当做一种更安全的方式,用来代替类型T的引用。这种引用要么引用某个对象,要么为null。但是只有在正确使用的情况下才会更安全。

有效地使用Optional的关键是要使用这样的方法,它的值不存在的情况下回产生一个可替代物,而只有在值存在的情况下才会使用这个值。

代码语言:javascript复制
// 产生这个Optional的值,或者在改Optional为空时,产生other
T orElse(T other)

// 产生这个Optional的值,或者在改Optional为空时,产生调用other的结果
T orElse(Supplier<? extends T> other)

// 产生这个Optional的值,或者在该Optional为空时,抛出调用exceptionSupplier
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)

// 如果该Optional不为空,那么就将它的值传递给consumer
void ifPresent(Consumer<? super T> consumer)

//产生将该Optional的值传递给mapper后的结果,只要这个Optional不为空且结果不为null,否则产生一个空Optional
<U> Optional<U> map(Function<? super T, ? extends U> mapper)

如果没有正确地使用Optional值,那么相比较以往得到的“某物或null”的方式,并没有得到任何好处。

get方法会在Optional值存在的情况下获得其中包装的元素,或者在不存在的情况下抛出一个NoSuchElementException对象。

代码语言:javascript复制
// 产生这个Optional的值,或者在改Optional为空时,抛出一个NoSuchElementException对象
T get()

//如果该Optional不为空,则返回true
boolean isPresent()

可以使用Optional.of(result)或者Optional.empty()等方法来创建Optional对象。

代码语言:javascript复制
// 产生一个具有给定值的Optional。如果value为null,那么第一个方法会抛出一个NullPointerException对象,而第二个方法会产生一个空Optional
static <T> Optional<T> of(T value)
static <T> Optional<T> ofNullable(T value)

// 产生一个空Optional
static <T> Optional<T> empty()

假设有一个可以产生Optional<T>对象的方法f,并且目标类型T具有一个可以产生Optional<U>的对象的方法g。如果它们都是普通方法,那么你可以通过调用s.f().g()来将它们组合,大使这种组合返回的类型为Optiona<T>而不是T。

代码语言:javascript复制
// 如果s.f()的值存在,那么g就可以应用到上面,否则返回一个空的Optional<U>
Optional<U> result=s.f().flatMap(T::g);
代码语言:javascript复制
// 产生将mapper应用于当前的Optional值所产生的结果,或者在当前Optional为空时,返回一个空Optional
<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)

处理完流之后,通常会想要查看其元素,此时可以调用iterator方法,会产生可以用来访问元素的旧式风格的迭代器,是一种终结操作。

代码语言:javascript复制
// java.util.stream.Collectors 8
// 产生一个将元素收集到列表或集中的收集器。
static <T> Collector<T,?,List<T>> toList()

//产生一个将元素收集到任意集合中的收集器,可以传递一个诸如TreeSet::new的构造器引用
static <T,C extends Collection<T>> Collector<T,?,C> toCollection(Supplier<C> collectionFactory)

//产生一个连接字符串的收集器。分隔符会置于字符串之间,而第一个字符串之前可以有前缀,最后一个字符串之后可以有后缀。如果没有指定,那么它们都为空
static Collector<CharSequence,?,String> joining()
static Collector<CharSequence,?,String> joining(CharSequence delimiter)
static Collector<CharSequence,?,String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)

//产生能够生成(Int|Long|Double)SummaryStatistics对象的收集器,通过它可以获得将mapper应用于每个元素后产生结果的个数、总和、平均值、最大值和最小值
static<T> Collector<T,?,InsSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper)
static<T> Collector<T,?,LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper)
static<T> Collector<T,?,DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper)

// IntSummaryStatistics 8
// LongSummaryStatistics 8
// DoubleSummaryStatistics 8
// 产生汇总后的元素个数
long getCount()
// 产生汇总后的元素的总和或平均值
(int|long|double) getSum()
double getAverage()
// 产生汇总后的元素的最大值和最小值,或者在每有任何元素时,产生(Integer|Long|Double).(MAX|MIN)_VALUE
代码语言:javascript复制
// 产生一个收集器,它会产生一个映射表或并发映射表。keyMapper和valueMapper函数会应用于每个收集到的元素上,从而在所产生的映射表中生成一个键/值项。默认情况下,当两个元素产生相同的键时,会抛出一个IllegalStateException异常。你可以提供一个mergeFunction来合并具有相同键的值。默认情况下,其结果是一个HashMap或ConcurrentHashMap。你可以提供一个mapSupplier,它会产生所期望的映射表实例
static<T,K,U> Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,Function<? super T, ? extends U> valueMapper)
static<T,K,U> Collector<T,?Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)
static <T,K,U,M extends Map<K,U>> Collector<T,?,M> toMap(Function<? super T, ?extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
static <T,K,U> Collector<T,?,ConcurrentMap<K,U>> toConcurrentMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)
static <T,K,U> Collector<T,?,ConcurrentMap<K,U>> toConcurrentMap(Function<? super T, ? extends K> keyMapper, Function<? super T,? extends U> valueMapper,BinaryOperator<U> mergeFunction)
static <T,K,U,M extends ConcurrentMap<K,U>> Collector<T,?,M> toConcurrentMap(Function<? super T,? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
代码语言:javascript复制
// 产生一个收集器,它会产生一个映射表或并发映射表,其键是将classifier应用于所有收集到的元素上所产生的结果,而值时由具有相同键的元素构成的一个个列表
static<T,K> Collector<T,?,Map<K,List<T>>> groupingBy(Function<? super T,? extends K> classifier)
static <T,K> Collector<T,?,ConcurrentMap<K,List<T>>> groupingByConcurrent(Function<? super T,? extends K> classifier)

// 产生一个收集器,它会产生一个映射表,其键是true/false,而值是由满足/不满足断言的元素构成的列表
static <T> Collector<T,?,Map<Boolean,List<T>>> partitioningBy(Predicate<? super T> predicate)

groupingBy方法会产生一个映射表,它的每个值都是一个列表,如果想要以某种方式来处理这些列表,就需要提供一个下游收集器。例如,如果想要获得集而不是列表,那么可以用Collector.toSet收集器。

代码语言:javascript复制
// 产生一个可以对收集到的元素进行计数的收集器
static <T> Collector<T,?,Long> counting()

// 产生一个收集器,对将mapper应用到收集到的元素上之后产生的值计算总和
static <T> Collector<T,?,Integer> summingInt(ToIntFunction<? super T> mapper)
static <T> Collector<T,?,Long> summingLong(ToLongFunction<? super T> mapper)
static <T> Collector<T,?,Double> summingDouble(ToDoubleFunction<? super T> mapper)

// 产生一个收集器,使用comparator指定的排序方法,计算收集到的元素中的最大值和最小值
static <T> Collector<T,?,Optional<T>> maxBy(Comparator<? super T> comparator)
static <T> Collector<T,?,Optional<T>> minBy(Comparator<? super T> comparator)

//产生一个收集器,它会产生一个映射表,其键是将mapper应用到收集到的数据上而产生的,其值是使用downstream收集器收集到具有相同键的元素
static <T,U,A,R> Collector<T,?,R> mapping(Function<? super T,? extends U> mapper, Collector<? super U,A,R> downstream)

reduce方法是一种用于从流中计算某个值的通用机制,其最简单的形式将结构一个二元函数,并从前两个元素开始持续应用它。

代码语言:javascript复制
// 用给定的accumulator函数产生流中元素的累积总和。如果提供了幺元,那么第一个被累计的元素就是该幺元。如果提供了组合器,那么它可以用来将分别累积各个部分整合成总和
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce (T identity, BinaryOperator<T> accumulator)
<U> U reduce(U identity, BiFunction<U,? super T, U> accumulator, BinaryOperator<U> combiner)

// 将元素收集到类型R的结果中。在每个部分上,都会调用supplier来提供初始结果,调用accumulator来交替地将元素添加到结果中,并调用combiner来整合两个结果
<R> Rcollect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)

基本类型流上的方法与对象流上的方法类似,最主要的差异如下:

  • toArray方法会返回基本类型数组
  • 产生可选结果的方法会返回一个OptionalInt、OptionalLong或OptionalDouble。这些类与Optional类类似,但是具有getAsInt、getAsLong、getAsDouble方法而不是get方法
  • 具有返回总和、平均值、最大值和最小值的sum、average、max和min方法。对象流没有具体定义这些方法
  • summaryStatistics方法会产生一个类型为IntSummaryStatistics、LongSummaryStatistics或DoubleSummaryStatistics的对象。它们可以同时报告流的总和、平均值、最大值和最小值。

流使得并行处理块操作变得很容易,这个过程几乎是自动的,但是需要遵守一些规则。只要在终结方法执行时,流处于并行模式,那么所有中间流操作都将被并行化。

为了让并行流正常工作,需要满足大量的条件:

  • 数据应该在内存中。必须等到数据到达时非常低效的
  • 流应该可以被高效地分成若干个子部分。由数组或平衡二叉树支撑的流都可以工作的很好,但是Stream.iterate返回的结果不行
  • 流操作的工作量应该具有较大规模。如果总工作负载并不是很大,那么搭建并行计算时所付出的代价就没有什么意义
  • 流操作不应该被阻塞
代码语言:javascript复制
// 产生一个与当前流中元素相同的并行流
S parallel()

//产生一个与当前流中元素相同的无序流
S unordered()

// 用当前集合中的元素产生一个并行流
Stream<E> parallelStream() 

0 人点赞