JDK1.8新特性:流的学习

一:为什么要使用流

1.流的使用比循环版本要易于阅读,我们不必扫描整个代码去查找过滤和计数操作,方法名就可以直接告诉我们其代码意欲何为。
2.循环需要非常详细地指定操作的顺序,而流能够以其想要的任何方式来调度操作,只要结果正确即可。

二:流与集合的差异

1.流并不存储其元素。这些元素可能存储在底层的集合中,或者是按需生成的。
2.流的操作不会修改其数据源。例如,filter方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素。
3.流的操作的尽可能惰性执行的。意味着直至需要其结果时操作才会执行。

三:创建一个工作流的典型案例

 long count = wordList.stream()
             .filter(w -> w.length() > 5)
             .count();

1.创建一个流
2.指定将初始流转换为其他流的中间操作,可能宝行多个步骤。
3.应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作。之后这个流就再也不能使用。

四:流的创建

1.java.util.stream.Stream (jdk 8)

 static<T> Stream<T> of(T... values) //产生一个元素为给定的流
 static<T> Stream<T> empty() //产生一个不包含任何元素的流
 static<T> Stream<T> generate(Supplier<T> s) //产生一个无线流,它的值是通过反复调用函数s而构建的。
 static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
 //产生一个无线流,它的元素包含种子、在种子上调用f产生的值、在前一个元素上调用f产生的值。

2.java.util.Arrays(jdk 1.2)

static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive)//产生一个流,它的元素是由数组中指定范围内的元素构成的。
static IntStream stream(int[] array)//int类型的流
static IntStream stream(int[] array, int startInclusive, int endExclusive)
static LongStream stream(long[] array)//long类型的流
static LongStream stream(long[] array, int startInclusive, int endExclusive)
static DoubleStream stream(double[] array)//double类型的流
static DoubleStream stream(double[] array, int startInclusive, int endExclusive) 

3.java.util.regex.Pattern (jdk 1.4)

Stream<String> splitAsStream(CharSequence input) 
//产生一个流,它的元素是输入中由该模式界定的部分。

4.java.nio.file.Files (jdk 7)

static Stream<String> lines(Path path)
static Stream<String> lines(Path path , Charset cs)
//产生一个流,它的元素是指定文件中的行,该文件的字符集为UTF-8,或者为指定的字符集

5.java.util.function.Supplier (jdk 8)

T get()
提供一个值

五:流的转换

1.filter方法

Stream<T> filter(Predicate<? super T> predicate)
//产生一个流,它包含当前流中所有满足断言条件的元素。
//filter的引元是Predicate<T>,即从T到boolean的函数。

2.map方法

通常,如果想要按照某种方式来转换流中的值,可以使用map方法并传递执行该转换函数(lambda表达式代替)。
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
//产生一个流,它包含将mapper应用于当前流中所有元素产生的结果。
IntStream mapToInt(ToIntFunction<? super T> mapper)
LongStream mapToLong(ToLongFunction<? super T> mapper)
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)

在使用map时,会有一个函数应用到每个元素上,并且其结果是包含了应用该函数后所有产生的所有结果的流。

3.flatMap方法

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

4.抽取子流

Stream<T> limit(long maxSize)
//调用limit函数后会返回一个新的流,它在n个元素之后结束
//如原来的流长度小于n那么就会在流结束时结束。
//可用于裁剪无限流的尺寸。
Stream<T> skip(long n)
//调用skip函数后会返回一个新的流,它的元素是当前流中除了前面n个元素之外的所有元素。
//与limit正好相反

5.连接流

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

6.其他流转换

 Stream<T> distinct()
 //产生一个流,包含当前流中所有不同的元素
 //原来的元素按照同样的顺序剔除重复元素
 Stream<T> sorted() //要求元素实现了Comparable的类实例
 Stream<T> sorted(Comparator<? super T> comparator);
 //产生一个流,它的元素是当前流中的所有元素按照顺序排列的
Stream<T> peek(Consumer<? super T> action)
//产生一个流,它与当前流中的元素相同,在获取其中每个元素时,会将其传递给action

六:流的约简(终结操作)

约简是一种终结操作,它们会将流约简为可以在程序中使用的非流值。有的简约操作返回Optional值,或者在其中包装了答案。(Optional类型是一种更好的表现缺少返回值的方式,可避免直接返null值导致空指针异常。)

long count()
//返回流中元素的个数
Optional<T> max(Comparator<? super T> comparator)
Optional<T> min(Comparator<? super T> comparator)
//分别产生这个流的最大元素与最小元素,使用由给定比较器定义的排序规则,如果这个流为空,会产生一个空的Optional对象。
Optional<T> findFirst()
Optional<T> findAny()
//分别产生这个流的第一个和任意一个元素,如果这个流为空,会产生一个空的Optional对象。
boolean anyMatch(Predicate<? super T> predicate)
boolean allMatch(Predicate<? super T> predicate)
boolean noneMatch(Predicate<? super T> predicate)
//分别在这个流中任意元素、所有元素和没有任何元素匹配给定断言时返回true

七:Optional类型

Optional《T》对象时一种包装器对象,要么包装了T类型的对象,要么没有包装任何对象。对于第一种情况我们称这种值为存在。Optional类型被当作一种更安全的方式,用于替代类型T的应用,这个引用要么引用某个对象,要么为null。但是其只有在正确的使用下才会更安全。

optional:值存在的情况下才会使用这个值,值不存在的情况下会产生一个可替代物。

1.optional值的使用

T orElse(T other)
//产生这个optional的值,当值为空时,返回other
T orElseGet(Supplier<? extends T> other)
//产生这个optional的值,当值为空时,产生调用other的结果
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)
//产生这个optional的值,当值为空时抛出exceptionSupplier的结果
void ifPresent(Consumer<? super T> consumer)
//如果optional的值不为空,就把值传递给consumer。若值为空不进行任何操作。
<U> Optional<U> map(Function<? super T, ? extends U> mapper)
//产生将该optional的值传递给mapper后的结果,只要optional的值不为空且结果不为null,否则产生一个空的optional

2.不适合使用optional值的情况判断

T get()
//产生这个optional的值,或者在该值为空时,抛出NoSuchElementException
boolean isPresent()
//如果optional不为空,返回true
//在调用get方法之前可以先用isPresent方法做一下判断。

3.创建Optional的值

static <T> Optional<T> of(T value)
static <T> Optional<T> ofNullable(T value)
//产生一个具有给定值的optional对象。如果value值为null时,of方法会抛出空指针异常,ofNullable会产生一个空的optional对象。
static<T> Optional<T> empty()
//产生一个空的optional对象。

4.用flatMap来构建Optional值的函数

<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
//产生将mapper应用于当前的Optional值产生的结果,或者当前Optional为空时返回一个空的Optional

八:收集结果(终结操作)

1.java.util.stream.BaseStream (jdk 8)

Iterator<T> iterator()
//产生一个用于获取当前流中各个元素的迭代器。这是一个终结操作

2.java.util.stream.Stream (jdk 8)

void forEach(Consumer<? super T> action)
//在流的每个元素上调用action。这是一个终结操作
//在并行流上,forEach方法会议任意顺序遍历各个元素。
void forEachOrdered(Consumer<? super T> action);
//顺序遍历并行流可以使用,这个方法会丧失并行处理的能力与优势。
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
//产生一个对象数组,或者在将引用A[]::new传递给构造器时,返回一个A类型的数组,这些操作都是终结操作。
<R, A> R collect(Collector<? super T, A, R> collector);
//使用给定的收集器来收集当前流中的元素。Collectors类有用于多种收集器的工厂方法。

2.Collectors(java.util.stream.Collectors)类中用于多种收集器的工厂方法

Collector<T, ?, List<T>> toList()
Collector<T, ?, Set<T>> toSet()
//产生一个将元素收集到列表或集中的收集器
static <T, C extends Collection<T>> Collector<T, ?, C> toCollection(Supplier<C> collectionFactory)
//产生一个将元素收集到任意集合中的收集器,例如可以传递TreeSet::new的构造器引用
static Collector<CharSequence, ?, String> joining()
static Collector<CharSequence, ?, String> joining(CharSequence delimiter)
static Collector<CharSequence, ?, String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
//产生一个连接字符串的收集器。分隔符会置于字符串之间,第一个字符可有前缀,最后一个可以有后缀。如果没指定则都为空。
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)
//产生能够生成Int/Long/Double SummaryStatistics对象的收集器,通过它可以获得将mapper应用与每个元素后产生的结果个数、总和、平均值、最大值和最小值。

java.util.IntSummaryStatistics
java.util.LongSummaryStatistics
java.util.DoubleSummaryStatistics
中的方法

final long getCount()
final long getSum()
final int getMin()
final int getMax()
final double getAverage()

3.收集到映射表中

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)                                                    

//产生一个收集器,它会产生一个映射表或者并发映射表
keyMapper和valueMapper函数会应用于每个收集到的元素上,
从而在产生的映射表中生成一个键/值项。
默认情况下,当两个元素产生相同的key值时,会抛出IllegalStateException异常。
可以提供一个mergeFunction用来处理相同的key值。
默认情况下结果是一个HashMap或者ConcurrentHashMap。你可以提供一个mapSupplier,产生所期望的映射表实例。

4.群组和区分

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)
//产生一个收集器,它会产生一个映射表或者并发映射表,其键是将classifier应用于所有收集到元素上产生的结果,而值是由具有相同键的元素构成的一个个列表。
static <T>
    Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate)
//键值是true/false,值是满足/不满足断言的元素组成的列表。

5.下游收集器
groupingBy方法会产生一个映射表,它的每个值都是一个列表。如果想要以某种方式来处理列表,就需要提供一个下游收集器

java.util.stream.Collectors

static <T> Collector<T, ?, Long> counting()
//产生一个可以对收集到的元素进行计数的收集器
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)
//产生一个收集器,对将mapper应用到收集到的元素上之后产生的值计算总和。
static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator)
static <T> Collector<T, ?, Optional<T>>maxBy(Comparator<? super T> comparator)
//产生一个收集器,使用comparator指定的排序方法,计算收集到元素中的最大值和最小值。
static <T, U, A, R>
    Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
                               Collector<? super U, A, R> downstream)
//产生一个收集器,它会产生一个映射表,其键是将mapper应用到收集到的数据上而产生的,其值是使用downstream收集器收集到的具有相同键的元素。

6.简约操作

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)
用给定的accumulator函数产生流中元素的累积总和。如果提供了幺元,那么第一个被累计的元素就是该幺元。如果提供了组合器,那么它可以用来将分别累计各个部分整合成总和。

九:基本类型流

对于基本数据类型,流库中具有专门的类型IntStream、LongStream、DoubleStream,用来直接存储基本数据类型,无需使用包装器。
IntStream:int / byte / boolean / char / short
LongStream:long
DoubleStream:float / double

十:并行流

流使得并行处理操作变得很容易,过程几乎是自动的,但是要遵守一些规则。
1.必须拥有一个并行流

Collection.parallelStream()//可以从任何集合中获取一个并行流

2.只要在终结方法执行时,流处于并行模式,那么所有的中间流操作都将被并行化。
3.当流操作并行运行时,其目的是要让其返回结果与顺序执行时返回的结果相同。重要的是这些操作可以以任意的顺序。
4.默认情况下,从有序集合、范围、生成器和迭代产生的流,或者通过调用Stream.sorted产生的流都是有序的。

并行流工作需要满足的条件:

  1. 数据应该在内存中。必须等到数据到达是非常低效的
  2. 流应该可以被高效地分成若干子部分。由数组或者平衡二叉树支撑的流都可以工作的很好,但是stream.iterate返回的结果不行。
  3. 流操作的工作量应该具有较大的规模。如果总工作负载并不是很大,那么搭建并行计算时所付出的代价没有什么意义。
  4. 流操作不应该被阻塞。

参考书籍:Java 核心技术 卷Ⅱ

猜你喜欢

转载自blog.csdn.net/zhangsuhua0702/article/details/82556078