流是一系列与特定存储无关的元素,实际上流没有存储之说
大多数的集合存储对象都是为了处理它, 而流是直接处理它, 跳过了存储的这一环节,使得代码更加短小精悍,更易理解
另一方面 , 流是懒加载的, 也就是说只有在必要的情况下才会进行, 你可以将流看做一个延迟列表,由于计算延迟, 流使我们可以表示一个非常大的序列, 而不需要去考虑内存

流支持

在设计流时, java的设计者面临的一个难题就是, 现存的大量类库,不仅被java使用, 还没数以百万记的java生态圈代码中, 如何将一个全新的流概念融入现有的类库中呢?

比如在 Random 中添加更多的方法。只要不改变原有的方法,现有代码就不会受到干扰。
一个大的挑战来自于使用接口的库。集合类是其中关键的一部分,因为你想把集合转为流。但是如果你将一个新方法添加到接口,那就破坏了每一个实现接口的类,因为这些类都没有实现你添加的新方法。

java8 是通过在现有的类库中加入default修饰的默认方法, 通过这种方案,设计者可以将流式平滑的嵌入到现有的类库中

流操作的类型

创建流, 修改流元素, 消费流元素
消费流元素通常是指收集流元素到集合中

流创建

1.stream.of
可以通过 Stream.of() 很容易地将一组元素转化成为流

  1. // streams/StreamOf.java
  2. import java.util.stream.*;
  3. public class StreamOf {
  4. public static void main(String[] args) {
  5. Stream.of(new Bubble(1), new Bubble(2), new Bubble(3))
  6. .forEach(System.out::println);
  7. Stream.of("It's ", "a ", "wonderful ", "day ", "for ", "pie!")
  8. .forEach(System.out::print);
  9. System.out.println();
  10. Stream.of(3.14159, 2.718, 1.618)
  11. .forEach(System.out::println);
  12. }
  13. }
  14. 输出
  15. Bubble(1)
  16. Bubble(2)
  17. Bubble(3)
  18. It's a wonderful day for pie!
  19. 3.14159
  20. 2.718
  21. 1.618

2.可以用.stream将集合成流

// streams/CollectionToStream.java
import java.util.*;
import java.util.stream.*;
public class CollectionToStream {
    public static void main(String[] args) {
        List<Bubble> bubbles = Arrays.asList(new Bubble(1), new Bubble(2), new Bubble(3));
        System.out.println(bubbles.stream()
            .mapToInt(b -> b.i)
            .sum());

        Set<String> w = new HashSet<>(Arrays.asList("It's a wonderful day for pie!".split(" ")));
        w.stream()
         .map(x -> x + " ")
         .forEach(System.out::print);
        System.out.println();

        Map<String, Double> m = new HashMap<>();
        m.put("pi", 3.14159);
        m.put("e", 2.718);
        m.put("phi", 1.618);
        m.entrySet().stream()
                    .map(e -> e.getKey() + ": " + e.getValue())
                    .forEach(System.out::println);
    }
}

输出
6
a pie! It's for wonderful day
phi: 1.618
e: 2.718
pi: 3.14159

map( ) :

获取对象并产生新对象, ( )可空, 非空时里面填的是产生新对象的方法

mapToInt :

获取对象并转成一个intSteam, 也有对象的double,float

.collect(toList) :

将流生成一个list列表

Collectors.joining() :

流中的元素被joining( )的参数分割

.sum( ) :

求和

rang(a, b) :

取a到b的 [a, b) a跟b都是数字

Stream.generate()

我们提前看到了 Stream.generate() 的用法,它可以把任意 Supplier<T> 用于生成 T 类型的流。generate()的()中放的是要生成的类型,或者是一个要生产那些对象的lambda表达式或者生成那个对象的方法引用,也就是说Stream.generate() 是控制生成对象类型用的

斐波那契数列: 从第三项开始,每一项等于前两项的和

iterate()

Stream.iterate() 产生的流的第一个元素是种子(iterate方法的第一个参数),然后将种子传递给方法(iterate方法的第二个参数)。方法运行的结果被添加到流(作为流的第二个元素),并存储起来作为下次调用 iterate()时的第一个参数,以此类推。

流的建造者模式

首先先创建一个builder对象, 然后将创建流所需的多个信息传给他, 最后执行builder对象的build方法来创建

跟踪与调试

peek()

允许你无修改的查看流中的元素

filter()

若元素传递给过滤函数产生的结果为true,即()中的表达式为true ,则过滤操作保留这些元素。

distinct():

在Randoms.java包的distinct()可以用来消除流中的重复元素, 功能类似于set, 但是开销比set小很多

应用函数到元素

  • map(Function):将函数操作应用在输入流的元素中,并将返回值传递到输出流中。
  • mapToInt(ToIntFunction):操作同上,但结果是 IntStream
  • mapToLong(ToLongFunction):操作同上,但结果是 LongStream
  • mapToDouble(ToDoubleFunction):操作同上,但结果是 DoubleStream

map() 中组合流

map()的返回值为一个元素 , 但是会遇到返回值也是一个流, 且这个流的返回值才是元素这种情况, 这时候就需要用到flatMap(), 它能将每个输出流扁平化为元素,使输出的仅为元素
flatMap(Function):当 Function 产生流时使用。
flatMapToInt(Function):当 Function 产生 IntStream 时使用。
flatMapToLong(Function):当 Function 产生 LongStream 时使用。
flatMapToDouble(Function):当 Function 产生 DoubleStream 时使用。
也就是说,它会自动把流拍平后的元素输出出来

// streams/StreamOfStreams.java
import java.util.stream.*;
public class StreamOfStreams {
    public static void main(String[] args) {
        Stream.of(1, 2, 3)
        .map(i -> Stream.of("Gonzo", "Kermit", "Beaker"))
        .map(e-> e.getClass().getName())
        .forEach(System.out::println);
    }
}
输出
java.util.stream.ReferencePipeline$Head
java.util.stream.ReferencePipeline$Head
java.util.stream.ReferencePipeline$Head

// streams/FlatMap.java
import java.util.stream.*;
public class FlatMap {
    public static void main(String[] args) {
        Stream.of(1, 2, 3)
        .flatMap(i -> Stream.of("Gonzo", "Fozzie", "Beaker"))
        .forEach(System.out::println);
    }
}

输出
Gonzo
Fozzie
Beaker
Gonzo
Fozzie
Beaker
Gonzo
Fozzie
Beaker


Optional类

如果一个流中可能出现为空的情况, 会导致异常, 而optional类就是被设计用来解决空的问题

findFirst()返回一个包含第一个元素的optional对象, 如果为空 返回optional.empty
findAny()返回一个包含任意元素的optional对象,如果返回为空 返回optional.empty
max()、min()返回一个包含最大值或最小值的optional对象, 如果流为空 返回optimal.empty

// streams/OptionalsFromEmptyStreams.java
import java.util.*;
import java.util.stream.*;
class OptionalsFromEmptyStreams {
    public static void main(String[] args) {
        System.out.println(Stream.<String>empty()
             .findFirst());
        System.out.println(Stream.<String>empty()
             .findAny());
        System.out.println(Stream.<String>empty()
             .max(String.CASE_INSENSITIVE_ORDER));
        System.out.println(Stream.<String>empty()
             .min(String.CASE_INSENSITIVE_ORDER));
        System.out.println(Stream.<String>empty()
             .reduce((s1, s2) -> s1 + s2));
        System.out.println(IntStream.empty()
             .average());
    }
}
输出
Optional.empty
Optional.empty
Optional.empty
Optional.empty
Optional.empty
OptionalDouble.empty

reduce()就是用来进行加 减 最大 最小的
Stream.empty() 是用来产生一个空流, 注意泛型不能省略

// streams/OptionalBasics.java
import java.util.*;
import java.util.stream.*;
class OptionalBasics {
    static void test(Optional<String> optString) {
        if(optString.isPresent())
            System.out.println(optString.get()); 
        else
            System.out.println("Nothing inside!");
    }
    public static void main(String[] args) {
        test(Stream.of("Epithets").findFirst());
        test(Stream.<String>empty().findFirst());
    }
}
输出
Epithets
Nothing inside!

当你接收到 Optional 对象时,应首先调用 isPresent() 检查其中是否包含元素。如果存在,可使用 get() 获取。

便利函数

有很多便利函数可以解包Optional, 这就简化了“对所包含的对象的检验和执行操作”的过程

  • ifPresent(Consumer):当值存在时调用 Consumer,否则什么也不做。
  • orElse(otherObject):如果值存在则直接返回,否则生成 otherObject
  • orElseGet(Supplier):如果值存在则直接返回,否则使用 Supplier 函数生成一个可替代对象。
  • orElseThrow(Supplier):如果值存在直接返回,否则使用 Supplier 函数生成一个异常。

创建 Optional

Optional.empty() 生成一个空的optional
Optional.of(value)将非空的value包装成optional
Optional.ofNullable(value)value可能为空, 空的时候返回Optional.empty

Optional 对象操作

当我们的流管道生成Optional对象时, 有三种方法可以使Optional对象的后续做到更多的操作

  • filter(Predicate):对 Optional 中的内容应用Predicate 并将结果返回。如果 Optional 不满足 Predicate ,将 Optional 转化为空 Optional 。如果 Optional 对象已经为空,则直接返回空Optional 。一般的来说,普通的filte(function)会直接移除对应不满足function(也就是返回值为false时)的元素,而optional.filter(function)则是直接将对应的optional对象设为空,
  • map(function), 如果流对象非空则执行function,否则返回optional.empty, 返回的对象会自动被包装成Optional
  • flatMap(Function):同 map(),但是提供的映射函数将结果包装在 Optional 对象中,因此 flatMap() 不会在最后进行任何包装。很显然 Optional.flatMap() 是为那些自己已经生成 Optional 的函数而设计的

Optional 流

假设你的流可能会产生空值, 这时候就可以用Optional来包装你的元素

终端操作

以下操作将会获取流的最终结果, 至此我们将不再能往后传递流, 也就是说终端操作总是在我们流管道操作的最后一件事

数组

  • toArray():将流转换成适当类型的数组。
  • toArray(generator):在特殊情况下,生成自定义类型的数组

循环

  • forEach(Consumer)常见如 System.out::println 作为 Consumer 函数。
  • forEachOrdered(Consumer): 保证 forEach 按照原始流顺序操作。

注意只有在并发编程中才会出现forEach()的无序操作
这里说下parallel(),它会将流进行分割, 分割成多个部分(分割个数取决于处理器个数),然后再不同的处理器上分别操作

// streams/ForEach.java
import java.util.*;
import java.util.stream.*;
import static streams.RandInts.*;
public class ForEach {
    static final int SZ = 14;
    public static void main(String[] args) {
        rands().limit(SZ)
                .forEach(n -> System.out.format("%d ", n));
        System.out.println();
        rands().limit(SZ)
                .parallel()
                .forEach(n -> System.out.format("%d ", n));
        System.out.println();
        rands().limit(SZ)
                .parallel()
                .forEachOrdered(n -> System.out.format("%d ", n));
    }
}
输出
258 555 693 861 961 429 868 200 522 207 288 128 551 589
551 861 429 589 200 522 555 693 258 128 868 288 961 207
258 555 693 861 961 429 868 200 522 207 288 128 551 589

结果可以发现 第一个跟第二个不一样, 因为每个处理器都是不一样的,而forEach()还是无趣的, 而第三个用了forEachOrdered(),这个让它按照流未使用parallel()的顺序来执行

集合

  • collect(Collector):使用 Collector 收集流元素到结果集合中。
  • collect(Supplier, BiConsumer, BiConsumer):同上,第一个参数 Supplier 创建了一个新的结果集合,第二个参数 BiConsumer 将下一个元素收集到结果集合中,第三个参数 BiConsumer 用于将两个结果集合合并起来。

可通过查看 java.util.stream.Collectors 的 API 文档了解。例如,我们可以将元素收集到任意一种特定的集合中。

组合

  • reduce(BinaryOperator):使用BinaryOperator来组合所有流中的元素, 因为流可能为空,其返回值为Optional
  • reduce(identity, BinaryOperator): 功能同上, identity做其组合的初始值, 如果流为空则返回identity ``` // streams/Reduce.java import java.util.; import java.util.stream.; class Frobnitz { int size; Frobnitz(int sz) { size = sz; } @Override public String toString() {
      return "Frobnitz(" + size + ")";
    
    } // Generator: static Random rand = new Random(47); static final int BOUND = 100; static Frobnitz supply() {
      return new Frobnitz(rand.nextInt(BOUND));
    
    } } public class Reduce { public static void main(String[] args) {
      Stream.generate(Frobnitz::supply)
              .limit(10)
              .peek(System.out::println)
              .reduce((fr0, fr1) -> fr0.size < 50 ? fr0 : fr1)
              .ifPresent(System.out::println);
    
    } }

输出 Frobnitz(58) Frobnitz(55) Frobnitz(93) Frobnitz(61) Frobnitz(61) Frobnitz(29) Frobnitz(68) Frobnitz(0) Frobnitz(22) Frobnitz(7) Frobnitz(29)

因为组合也是终端操作,所以传到ifPresent()的时候只有一个Optional结果, 所以最后一条触发的println<br />在这个reduce中, fr0是上一次执行函数产生的结果,  fr1是当前传入的流, 当传入29时, 因为满足函数,所以导致之后的表达式结果恒为29,所以最终的结果是29

<a name="CsRSj"></a>
### 匹配

- `allMatch(Predicate)` :如果每一个元素提供给Predicate都返回为true, 则结果返回true,会在第一个false时停止执行计算
- `anyMatch(Predicate)`:如果有一个云素提供给Predicate都返回为true, 则返回结果为true,会在第一个true时停止执行计算
- `noneMatch(Predicate)`:如果每个元素提供给Predicate都返回为false, 则返回结果为true,会在第一个true时停止执行计算
<a name="iVht7"></a>
### 查找

- `findFirst()`:返回第一个流元素的 **Optional**,如果流为空返回 **Optional.empty**。
- `findAny(`:返回含有任意流元素的 **Optional**,如果流为空返回 **Optional.empty**。

// streams/SelectElement.java import java.util.; import java.util.stream.; import static streams.RandInts.*; public class SelectElement { public static void main(String[] args) { System.out.println(rands().findFirst().getAsInt()); System.out.println( rands().parallel().findFirst().getAsInt()); System.out.println(rands().findAny().getAsInt()); System.out.println( rands().parallel().findAny().getAsInt()); } } 输出 258 258 258 242

可以看出findFrist()是不论是否多线程并行,都返回第一个元素, 而findAny()则是非并行时返回第一个, 并行时返回其他的
<a name="xgexp"></a>
### 信息

- `count()`:流中的元素个数。
- `max(Comparator)`:根据所传入的 **Comparator** 所决定的“最大”元素。
- `min(Comparator)`:根据所传入的 **Comparator** 所决定的“最小”元素。
<a name="Sg9Vm"></a>
### 数字流信息

- `average()` :求取流元素平均值。
- `max()` 和 `min()`:数值流操作无需 **Comparator**。
- `sum()`:对所有流元素进行求和。
- `summaryStatistics()`:生成可能有用的数据。

// streams/NumericStreamInfo.java import java.util.stream.; import static streams.RandInts.; public class NumericStreamInfo { public static void main(String[] args) { System.out.println(rands().average().getAsDouble()); System.out.println(rands().max().getAsInt()); System.out.println(rands().min().getAsInt()); System.out.println(rands().sum()); System.out.println(rands().summaryStatistics()); } } 输出结果:

507.94 998 8 50794 IntSummaryStatistics{count=100, sum=50794, min=8, average=507.940000, max=998} ```