on java 8 第十四章 流式编程

集合优化了对象的存储, 流和对象的处理有关。


  • 声明式编程(Declarative programming)声明要做什么,而非怎么做的编程风格
  • 命令式编程 imperative form

  • 显式地编写迭代机制称为外部迭代
  • 流式编程采用内部迭代,这是流式编程的核心特性之一。

流是懒加载,由于计算延迟,流使我们能够表示非常大(甚至无限)的序列,而不需要考虑内存问题

package stream;

import java.util.Random;

public class Randoms {
    public static void main(String[] args) {
        new Random(47)
                .ints(5,20)
                .distinct()
                .limit(7)
                .sorted()
                .forEach(System.out::println);
    }
}
/*
6
10
13
16
17
18
19
 */

package stream;

import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;

public class ImperativeRandoms {
    public static void main(String[] args) {
        Random rand = new Random(47);
        SortedSet<Integer> rints = new TreeSet<>();
        while (rints.size() < 7){
            int r = rand.nextInt(20);
            if (r < 5)continue;
            rints.add(r);
        }
        System.out.println(rints);
    }
}
/*
[7, 8, 9, 11, 13, 15, 18]
 */

1 流支持

在接口中添加被 default(默认)修饰的方法。通过这种方案,设计者们可以将流式(stream)方法平滑地嵌入到现有类中。

流操作的类型有三种:
创建流,
修改流元素(中间操作, Intermediate Operations),
消费流元素(终端操作, Terminal Operations)。
最后一种类型通常意味着收集流元素(通常是到集合中)。

2 流创建

将一组元素转化成为流

package stream;

import java.util.stream.Stream;

public class SteamOf {
    public static void main(String[] args) {
        Stream.of(new Bubble(1),new Bubble(2),new Bubble(3))
                .forEach(System.out::println);
        Stream.of("It's ","a ","wonderful ","day ","for ","pie! ")
                .forEach(System.out::print);
        System.out.println();
        Stream.of(3.14159,2.718,1.618)
                .forEach(System.out::println);
    }
}
/*
Bubble{i=1}
Bubble{i=2}
Bubble{i=3}
It's a wonderful day for pie! 
3.14159
2.718
1.618
 */

每个集合都可以通过调用 stream() 方法来产生一个流

package stream;

import java.util.*;

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.getKey())
                .forEach(System.out::println);
    }
}
/*
6
a pie! It's for wonderful day 
phi: phi
e: e
pi: pi
 */

2.1 随机数流

Random 类被一组生成流的方法增强

package stream;

import java.util.Random;
import java.util.stream.Stream;

public class RandomGenerators {
    public static <T> void show(Stream<T> stream){ // 基本指导原则:尽量使用泛型方法
        stream
        .limit(4)
        .forEach(System.out::println);
        System.out.println("+++++++");
    }

    public static void main(String[] args) {
        Random rand = new Random(47);
        show(rand.ints().boxed()); // boxed() 自动地把基本类型包装成为对应的装箱类型
        show(rand.longs().boxed());
        show(rand.doubles().boxed());
        //控制上限和下限
        show(rand.ints(10,20).boxed());
        show(rand.longs(10,20).boxed());
        show(rand.doubles(10,20).boxed());
        //控制流大小
        show(rand.ints(3,3,9).boxed());
        show(rand.longs(3,3,9).boxed());
        show(rand.doubles(3,3,9).boxed());
    }
}

使用 Random 为任意对象集合创建 Supplier

package stream;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class RandomWords implements Supplier<String> {
    List<String> words = new ArrayList<>();
    Random rand = new Random(47);
    RandomWords(String fname) throws IOException { // 构造函数中循环体使用命令式编程(外部迭代)
        List<String> lines = Files.readAllLines(Paths.get(fname));//通过 File 类将 Cheese.dat 文件的所有行读取到 List<String>

        for (String line : lines.subList(1,lines.size())){// 略过第一行
            for (String word : line.split("[ .?,]+")) //前面的东西可以出现一次或者多次
                words.add(word.toLowerCase());
        }
    }
    @Override
    public String get() {
        return words.get(rand.nextInt(words.size()));
    }

    @Override
    public String toString() {
        return words.stream()
                .collect(Collectors.joining(" ")); // collect() 收集操作
    }

    public static void main(String[] args) throws IOException {
        System.out.println(
                //Stream.generate() 的用法,可以把任意 Supplier<T> 用于生成 T 类型的流。
                Stream.generate(new RandomWords("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat"))
                .limit(10)
                .collect(Collectors.joining(" "))); // collect() 收集操作

    }
}
/*
it shop sir the much cheese by conclusion district is
 */

2.2 int 类型的范围

package stream;
import static java.util.stream.IntStream.*;

public class Ranges {
    public static void main(String[] args) {
        // IntStream 类提供 range() 方法用于生成整型序列的流。
        // 编写循环时,这个方法会更加便利
        int result = 0;
        for (int i = 10;i < 20; i++)
            result += i;
        System.out.println(result);

        // for-in循环
        result = 0;
        for (int i: range(10,20).toArray())
            result += i;
        System.out.println(result);

        // 使用流
        System.out.println(range(10,20).sum());
    }
}

实用小功能 repeat() 可以用来替换简单的 for 循环

package stream;
import static java.util.stream.IntStream.*;
public class Repeat {
    public static void repeat(int n,Runnable action){
        range(0,n).forEach(i->action.run());
    }
}
package stream;

public class Looping {
    static void hi(){
        System.out.println("Hi!");
    }

    public static void main(String[] args) {
        Repeat.repeat(3,()-> System.out.println("Looping!"));
        Repeat.repeat(2,Looping::hi);
    }
}

2.3 generate()

package stream;

import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Generator implements Supplier<String> {
    Random rand = new Random(47);
    char[] letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();

    @Override
    public String get(){
        return "" +letters[rand.nextInt(letters.length)];
    }

    public static void main(String[] args) {
        String word = Stream.generate(new Generator())
                .limit(30)
                .collect(Collectors.joining());
        System.out.println(word);
    }
}
/*
YNZBRNYGCFOWZNTCQRGSEGZMMJMROE
 */

创建包含相同对象的流,只需要传递一个生成那些对象的 lambda 到 generate() 中

package stream;

import java.util.stream.Stream;

public class Duplicator {
    public static void main(String[] args) {
        Stream.generate(()->"duplicate")
                .limit(3)
                .forEach(System.out::println);
    }
}
/*
duplicate
duplicate
duplicate
 */

bubbler() 与 Supplier 是接口兼容的

package stream;

import java.util.stream.Stream;

public class Bubbles {
    public static void main(String[] args) {
        Stream.generate(Bubble::bubbler)//创建单独工厂类(Separate Factory class)的另一种方式
                .limit(5)
                .forEach(System.out::println);
    }
}
/*
Bubble{i=0}
Bubble{i=1}
Bubble{i=2}
Bubble{i=3}
Bubble{i=4}
 */

2.4 iterate()

package stream;

import java.util.stream.Stream;

public class Fibonacci {
    int x = 1;

    Stream<Integer> numbers(){
        return Stream.iterate(0,i->{ // iterate() 只能记忆结果,因此我们需要利用一个变量 x 追踪另外一个元素。
            int result = x + i;
            x = i;
            return result;
        });
    }

    public static void main(String[] args) {
        new Fibonacci().numbers()
                .skip(20)//过滤前20个
                .limit(10)//取10个
                .forEach(System.out::println);
    }
}

2.5 流的建造者模式

建造者设计模式(也称构造器模式)

package stream;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class FileToWordsBuilder {
    Stream.Builder<String> builder = Stream.builder();

    public FileToWordsBuilder(String filePath) throws IOException {//在建造者设计模式(也称构造器模式)中,首先创建一个 builder 对象,传递给它多个构造器信息,最后执行“构造”。
        Files.lines(Paths.get(filePath))
                .skip(1)
                .forEach(line -> {
                    for (String w : line.split("[ .?,]+"))
                        builder.add(w);
                });
    }

    Stream<String> stream(){//只要你不调用 stream() 方法,就可以继续向 builder 对象中添加单词
//        builder.add("lala");
//        Stream<String> t = builder.build();
//        builder.add("lala2");
//        //在 Stream.Builder 调用 build() 方法后继续尝试添加单词会产生一个异常。
//        // java.lang.IllegalStateException

        return builder.build();
    }

    public static void main(String[] args) throws IOException {
        new FileToWordsBuilder("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat")
        .stream()
        .limit(7)
        .map(w ->w + " ")
        .forEach(System.out::print);
    }
}

2.6 Arrays

Arrays.stream 数组转化为流

 package stream;


public interface Operations {
    void execute();

    static void runOps(Operations... ops){
        for (Operations op: ops){
            op.execute();
        }
    }
    static void show(String msg){
        System.out.println(msg);
    }
}
package stream;

import java.util.Arrays;

public class Machine2 {
    public static void main(String[] args) {
        Arrays.stream(new Operations[] {
                () -> Operations.show("Bing"), // 重写 execute
                () -> Operations.show("Crack"),
                () -> Operations.show("Twist"),
                () -> Operations.show("Pop")
        }).forEach(Operations::execute);
    }
}
/*
Bing
Crack
Twist
Pop
 */
package stream;

import java.util.Arrays;

public class ArrayStreams {
    public static void main(String[] args) {
        Arrays.stream(new double[]{3.14159,2.718,1.618})
                .forEach(n->System.out.format("%f ",n));
        System.out.println();

        Arrays.stream(new int[]{1,3,5}).forEach(n->System.out.format("%d ",n));
        System.out.println();

        Arrays.stream(new long[]{11,22,44,66}).forEach(n->System.out.format("%d ",n));
        System.out.println();

        //选择一个子域  开始/停止
        Arrays.stream(new int[]{1,3,5,7,15,28,37},3,6).forEach(n ->System.out.format("%d ",n));
    }
}
/*
3.141590 2.718000 1.618000 
1 3 5 
11 22 44 66 
7 15 28 
 */

2.7 正则表达式

package stream;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FileToWordsRegexp {
    private String all;
    public FileToWordsRegexp(String filePath) throws IOException {
        all = Files.lines(Paths.get(filePath))
                .skip(1) //忽略第一行
                .collect(Collectors.joining(" "));
    }
    public Stream<String> stream(){
        return Pattern.compile("[ .,?]+").splitAsStream(all);
    }

    public static void main(String[] args) throws IOException {
        FileToWordsRegexp fw = new FileToWordsRegexp("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat");
        fw.stream()
                .limit(7)
                .map(w -> w + " ")
                .forEach(System.out::print);
        fw.stream()
                .skip(7)
                .limit(2)
                .map(w->w + " ")
                .forEach(System.out::print);
    }
}
/*
Not much of a cheese shop really is it 
 */
多次调用 stream() 在已存储的字符串中创建一个新的流。这里有个限制,整个文件必须存储在内存中;在大多数情况下这并不是什么问题,但是这损失了流操作非常重要的优势:

流“不需要存储”。当然它们需要一些内部存储,但是这只是序列的一小部分,和持有整个序列并不相同。
它们是懒加载计算的。

3 中间操作

中间操作用于从一个流中获取对象,

并将对象作为另一个流从后端输出,

以连接到其他操作。

3.1 跟踪和调试

peek() 操作的目的是帮助调试。它允许你无修改地查看流中的元素

Stream<T> peek(Consumer<? super T> action)

package stream;

import java.io.IOException;

public class Peeking {
    public static void main(String[] args) throws IOException {
        FileToWords.stream("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat")
                .skip(21)
                .limit(4)
                .map(w -> w + " ")
                .peek(System.out::print)
                .map(String::toUpperCase)
                .peek(System.out::print)
                .map(String::toLowerCase)
                .forEach(System.out::print);
    }
}
/*
Well WELL well it IT it s S s so SO so
 */

3.2 流元素排序

package stream;

import java.io.IOException;
import java.util.Comparator;

public class SortedComparator {
    public static void main(String[] args) throws IOException {
        FileToWords.stream("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat")
                .skip(10)
                .limit(10)
                .sorted(Comparator.reverseOrder()) // sorted() 预设了一些默认的比较器。这里我们使用的是反转“自然排序”。当然你也可以把 Lambda 函数作为参数传递给 sorted()
                .map(w -> w + " ")
                .forEach(System.out::print);
    }
}
/*
you what to the that sir leads in district And
*/

3.3 移除元素

package stream;
import java.util.stream.LongStream;

import static java.util.stream.LongStream.*;
public class Prime {
    public static Boolean isPrime(long n){ // 过滤器函数, 检测质数
        return rangeClosed(2,(long)Math.sqrt(n)).noneMatch(i -> n % i == 0);
    }
    public LongStream numbers(){
        return iterate(2,i-> i+1).filter(Prime::isPrime);
    }

    public static void main(String[] args) {
        new Prime().numbers()
                .limit(10)
                .forEach(n -> System.out.format("%d ",n));
        System.out.println();
        new Prime().numbers()
                .skip(99)
                .limit(10)
                .forEach(n-> System.out.format("%d ",n));
    }
}
/*
2 3 5 7 11 13 17 19 23 29 
541 547 557 563 569 571 577 587 593 599 
 */

3.4 应用函数到元素

package stream;

import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Stream;

public class FunctionMap {
    static String[] elements ={"12","","23","45"};
    static Stream<String> testStream(){
        return Arrays.stream(elements);
    }
    static void test(String descr, Function<String,String> func){
        System.out.println(" ---(" + descr +" )---");
        testStream()
                .map(func)
                // map(Function):将函数操作应用在输入流的元素中,并将返回值传递到输出流中。
                // map() 映射多种函数到一个字符串流中
                .forEach(System.out::println);
    }

    public static void main(String[] args) {
        test("add brackets",s->"[" + s +"]");
        test("Increment",s -> {
            try{
                return Integer.parseInt(s) + 1 + "";
            }catch (NumberFormatException e){
                //如果字符串不能转化成为整数就会抛出 NumberFormatException 异常,
                //我们只须回过头来将原始字符串放回到输出流中
                return s;
            }
        });
        test("Replace",s -> s.replace("2","9"));
        test("Take last digit",s-> s.length() > 0 ?
                s.charAt(s.length() - 1) + "":s );
    }
}
/*
 ---(add brackets )---
[12]
[]
[23]
[45]
 ---(Increment )---
13

24
46
 ---(Replace )---
19

93
45
 ---(Take last digit )---
2

3
5
 */

package stream;

import java.util.stream.Stream;

class Numbered{
    final int n;
    Numbered(int n){
        this.n = n;
    }

    @Override
    public String toString() {
        return "Numbered{" +
                "n=" + n +
                '}';
    }
}
public class FunctionMap2 {
    public static void main(String[] args) {
        Stream.of(15,7,9,11,13)
                .map(Numbered::new)
                .forEach(System.out::println);
    }
}
/*
将获取到的整数通过构造器 Numbered::new 转化成为 Numbered 类型
Numbered{n=15}
Numbered{n=7}
Numbered{n=9}
Numbered{n=11}
Numbered{n=13}
 */
package stream;

import java.util.stream.Stream;

public class FunctionMap3 {
    public static void main(String[] args) {
        Stream.of("5","7","9")
                .mapToInt(Integer::parseInt)
                .forEach( n -> System.out.format("%d ",n));
        System.out.println();
        Stream.of("17","19","23")
                .mapToLong(Long::parseLong)
                .forEach(n -> System.out.format("%d ",n));
        System.out.println();
        Stream.of("17","1.9",".23")
                .mapToDouble(Double::parseDouble)
                .forEach(n -> System.out.format("%f ",n));
    }
}
/*
5 7 9
17 19 23
17.000000 1.900000 0.230000
 */

3.5 在 map() 中组合流

flatMap() 做了两件事:将产生流的函数应用在每个元素上(与 map() 所做的相同),然后将每个流都扁平化为元素,因而最终产生的仅仅是元素。

package stream;

import java.util.stream.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
 */

package stream;

import java.util.stream.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
 */
package stream;

import java.util.Random;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamOfRandoms {
    static Random rand = new Random(47);

    public static void main(String[] args) {
        Stream.of(1,2,3,4,5)
                .flatMapToInt(i -> IntStream.concat(// concat(),它以参数顺序组合两个流
                        rand.ints(0,100).limit(i),IntStream.of(-1)
                )).forEach(n -> System.out.format("%d ",n));
    }
}
/*
58 -1 55 93 -1 61 61 29 -1 68 0 22 7 -1 88 28 51 89 9 -1
 */
package stream;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.regex.Pattern;
import java.util.stream.Stream;

public class FileToWords {
    public static Stream<String> stream(String filePath) throws IOException {
        return Files.lines(Paths.get(filePath))
                .skip(1)
//                .flatMap(line -> Arrays.stream(line.split("\W+")));
                .flatMap(line ->
                        Pattern.compile("\W+").splitAsStream(line));
        // \W+ 是一个正则表达式。他表示“非单词字符”,+ 表示“可以出现一次或者多次”。小写形式的 \w 表示“单词字符”
    }

}

package stream;

import java.io.IOException;

public class FileToWordsTest {
    public static void main(String[] args) throws IOException {
        FileToWords.stream("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat")
                .limit(7)
                .forEach(s -> System.out.format("%s ",s));
        System.out.println();
        FileToWords.stream("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat")
                .skip(7)
                .limit(2)
                .forEach(s -> System.out.format("%s ",s));
    }
}
/*
Not much of a cheese shop really
is it
 */

4 Optional类

对空流进行所有这些操作的简单测试
package stream;

import java.util.Optional;
import java.util.stream.Stream;

public 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());//空流是通过 Stream.<String>empty() 创建的
//        Stream<String> s = Stream.empty();
    }
}
/*
Epithets
Nothing inside
 */

4.1 便利函数

package stream;

import javax.swing.text.html.Option;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Stream;

public class Optionals {
    static void basics(Optional<String> optString){
        if (optString.isPresent())
            System.out.println(optString.get());
        else System.out.println("Nothing inside!");
    }
    static void ifPresent(Optional<String> optString){
        optString.ifPresent(System.out::println);
    }
    static void orElse(Optional<String> optString){
        System.out.println(optString.orElse("Nada"));
    }
    static void orElseGet(Optional<String> optString){
        System.out.println(optString.orElseGet(() -> "Generated"));
    }
    static void orElseThrow(Optional<String> optString){
        try{
            System.out.println(optString.orElseThrow(() -> new Exception("Supplied")));
        }catch (Exception e){
            System.out.println("Caught " + e);
        }
    }
    static void test(String testName, Consumer<Optional<String>> cos){
        System.out.println(" === " + testName + " === ");
        cos.accept(Stream.of("Epithets").findFirst());
        cos.accept(Stream.<String>empty().findFirst());
    }

    public static void main(String[] args) {
        test("basics",Optionals::basics);
        test("ifPresent",Optionals::ifPresent);
        test("orElse",Optionals::orElse);
        test("orElseGet",Optionals::orElseGet);
        test("orElseThrow",Optionals::orElseThrow);
    }
}
/*
ifPresent(Consumer):当值存在时调用 Consumer,否则什么也不做。
orElse(otherObject):如果值存在则直接返回,否则生成 otherObject。
orElseGet(Supplier):如果值存在则直接返回,否则使用 Supplier 函数生成一个可替代对象。
orElseThrow(Supplier):如果值存在直接返回,否则使用 Supplier 函数生成一个异常。

 === basics ===
Epithets
Nothing inside!
 === ifPresent ===
Epithets
 === orElse ===
Epithets
Nada
 === orElseGet ===
Epithets
Generated
 === orElseThrow ===
Epithets
Caught java.lang.Exception: Supplied
 */

4.2 创建-optional

package stream;

import java.util.Optional;

public class CreatingOptionals {
    static void test(String testName, Optional<String> opt){
        System.out.println(" === " + testName + " === ");
        System.out.println(opt.orElse("Null"));
    }

    public static void main(String[] args) {
        test("empty",Optional.empty());
        test("of",Optional.of("Howdy"));
        try{
            test("of",Optional.of(null));//不能通过传递 null 到 of() 来创建 Optional 对象。
        }catch (Exception e){
            System.out.println(e);
        }
        test("ofNullable",Optional.ofNullable("Hi"));
        test("ofNullable",Optional.ofNullable(null));// 最安全的方法是, 使用 ofNullable() 来优雅地处理 null
    }
}
/*
 === empty ===
Null
 === of ===
Howdy
java.lang.NullPointerException
 === ofNullable ===
Hi
 === ofNullable ===
Null
 */

4.3 optional-对象操作

filter(Predicate):将 Predicate 应用于 Optional 中的内容并返回结果。当 Optional 不满足 Predicate 时返回空。如果 Optional 为空,则直接返回。

map(Function):如果 Optional 不为空,应用 Function 于 Optional 中的内容,并返回结果。否则直接返回 Optional.empty。

flatMap(Function):同 map(),但是提供的映射函数将结果包装在 Optional 对象中,因此 flatMap() 不会在最后进行任何包装。
package stream;

import java.util.Arrays;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class OptionalFilter {
    static String[] elements = {"Foo", "", "Bar", "Baz", "Bingo"};
    static Stream<String> testStream(){
        return Arrays.stream(elements);
    }
    static void test(String descr, Predicate<String> pred){
        System.out.println(" ---(" + descr + " )---");
        for (int i = 0; i<= elements.length;i++){ // 最后一个元素实际上超出了流。将自动成为 Optional.empty
            System.out.println(testStream()
                    .skip(i)
                    .findFirst()// Optional<T> findFirst();
                    .filter(pred));
        }
    }

    public static void main(String[] args) {
        test("true", str -> true);
        test("false", str -> false);
        test("str != """, str -> str !=  "");
        test("str.length() == 3",str -> str.length() == 3);
        test("startsWith("B")",str -> str.startsWith("B"));
    }
}
/*
---(true )---
Optional[Foo]
Optional[]
Optional[Bar]
Optional[Baz]
Optional[Bingo]
Optional.empty
 ---(false )---
Optional.empty
Optional.empty
Optional.empty
Optional.empty
Optional.empty
Optional.empty
 ---(str != "" )---
Optional[Foo]
Optional.empty
Optional[Bar]
Optional[Baz]
Optional[Bingo]
Optional.empty
 ---(str.length() == 3 )---
Optional[Foo]
Optional.empty
Optional[Bar]
Optional[Baz]
Optional.empty
Optional.empty
 ---(startsWith("B") )---
Optional.empty
Optional.empty
Optional[Bar]
Optional[Baz]
Optional[Bingo]
Optional.empty
 */
package stream;

import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Stream;

public class OptionalMap {
    static String[] elements = {"12", "", "23", "45"};

    static Stream<String> testStream() {
        return Arrays.stream(elements);
    }

    static void test(String descr, Function<String, String> func) {
        System.out.println(" ---(" + descr + ")--- ");
        for (int i = 0; i <= elements.length; i++) {
            System.out.println(
                    testStream()
                            .skip(i)
                            .findFirst() //Optional
                            .map(func) //Optional 同 map() 一样 , Optional.map() 应用于函数。它仅在 Optional 不为空时才应用映射函数,并将 Optional 的内容提取到映射函数
            );
        }
    }

    public static void main(String[] args) {
        // If Optional is not empty, map() first extracts
        // the contents which it then passes
        // to the function:
        test("Add branckets", s -> "[" + s + "]");
        test("Increment", s -> {
            try {
                return Integer.parseInt(s) + 1 + "";
            } catch (NumberFormatException e) {
                return s;
            }
        });
        test("Replace", s -> s.replace("2", "9"));
        test("Take last digit", s -> s.length() > 0 ?
                s.charAt(s.length() - 1) + "" :s);
        // After the function is finished, map() wraps the
        // result in an Optional before returning it:

    }
}
/*
 ---(Add branckets)---
Optional[[12]]
Optional[[]]
Optional[[23]]
Optional[[45]]
Optional.empty
 ---(Increment)---
Optional[13]
Optional[]
Optional[24]
Optional[46]
Optional.empty
 ---(Replace)---
Optional[19]
Optional[]
Optional[93]
Optional[45]
Optional.empty
 ---(Take last digit)---
Optional[2]
Optional[]
Optional[3]
Optional[5]
Optional.empty
 */

package stream;

import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;

public class OptionalFlatMap {
    static String[] elements = {"12", "", "23", "45"};

    static Stream<String> testStream(){
        return Arrays.stream(elements);
    }

    static void test(String descr, Function<String, Optional<String>> func){
        System.out.println(" ---( " + descr + ")--- ");
        for (int i = 0; i <= elements.length; i++){
            System.out.println(
                    testStream()
                    .skip(i)
                    .findFirst()
                    .flatMap(func)
            );
        }
    }

    public static void main(String[] args) {
        test("Add brackets", s -> Optional.of("[" + s + "]"));
        test("Increment", s -> {
            try{
                return Optional.of(Integer.parseInt(s) + 1 + "");
            }catch (NumberFormatException e){
                return Optional.of(s);
            }
        });
        test("Replace", s -> Optional.of(s.replace("2", "9")));
        test("Take last digit", s -> Optional.of(s.length() > 0 ? s.charAt( s.length() -1) + "" : s));
    }

}

/*
Optional[[12]]
Optional[[]]
Optional[[23]]
Optional[[45]]
Optional.empty
 ---( Increment)---
Optional[13]
Optional[]
Optional[24]
Optional[46]
Optional.empty
 ---( Replace)---
Optional[19]
Optional[]
Optional[93]
Optional[45]
Optional.empty
 ---( Take last digit)---
Optional[2]
Optional[]
Optional[3]
Optional[5]
Optional.empty
 */

4.4 optional-流

package stream;

import java.util.Optional;
import java.util.Random;
import java.util.stream.Stream;

public class Signal {
    private final String msg;
    public Signal(String msg){this.msg = msg;}

    public String getMsg() {
        return msg;
    }

    @Override
    public String toString() {
        return "Signal(" + msg +
                ')';
    }
    static Random rand = new Random(47);
    public static Signal morse(){
        switch (rand.nextInt(4)){
            case 1:return new Signal("dot");
            case 2:return new Signal("dash");
            default:return null;
        }
    }
    public static Stream<Optional<Signal>> stream(){
        return Stream.generate(Signal::morse)
                .map(signal -> Optional.ofNullable(signal));

    }
}

package stream;

import java.util.Optional;

public class StreamOfOptionals {
    public static void main(String[] args) {
        Signal.stream()
                .limit(10)
                .forEach(System.out::println);
        System.out.println(" ---");
        Signal.stream()
                .limit(10)
                .filter(Optional::isPresent) //使用 filter() 来保留那些非空 Optional
                .map(Optional::get)//使用 get() 获取元素
                .forEach(System.out::println);
    }
}
/*
Optional[Signal(dash)]
Optional[Signal(dot)]
Optional[Signal(dash)]
Optional.empty
Optional.empty
Optional[Signal(dash)]
Optional.empty
Optional[Signal(dot)]
Optional[Signal(dash)]
Optional[Signal(dash)]
 ---
Signal(dot)
Signal(dot)
Signal(dash)
Signal(dash)
 */

5 终端操作

5.1 数组

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

package stream;

import java.util.Arrays;
import java.util.Random;
import java.util.stream.IntStream;

public class RandInts {
    private static int[] rints = new Random(47).ints(0,1000).limit(100).toArray();
    public static IntStream rands(){
        return Arrays.stream(rints);
    }
}

5.2 循环

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

package stream;
import static stream.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)); // parallel() 和 forEachOrdered() 来强制保持原始流顺序

    }
}
/*
258 555 693 861 961 429 868 200 522 207 288 128 551 589 
551 589 861 258 868 555 429 288 961 128 522 693 207 200 
258 555 693 861 961 429 868 200 522 207 288 128 551 589 
 */

5.3 集合

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

一个文件中的单词收集到 TreeSet 集合中

package stream;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

public class TreeSetOfWords {
    public static void main(String[] args) throws IOException {
        //转化成行流
        Set<String> words2 = Files.lines(Paths.get("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/TreeSetOfWords.java"))
                .flatMap( s -> Arrays.stream(s.split("\W+")))//匹配一个或多个非单词字符分割 使用Arrays.stream转化为流,将结果展平映射为单词流
                .filter( s -> !s.matches("\d+"))//查找并移除全数字字符串
                .map(String::trim)//去除单词两边空白
                .filter(s -> s.length() > 2)//过滤长度小于3的单词
                .limit(100)//只获取100个单词
                .collect(Collectors.toCollection(TreeSet::new));
        System.out.println(words2);

    }
}

在流中生成 Map

package stream;

import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Pair {
    public final Character c;
    public final Integer i;

    Pair(Character c, Integer i) {
        this.c = c;
        this.i = i;
    }

    public Character getC() {
        return c;
    }

    public Integer getI() {
        return i;
    }

    @Override
    public String toString() {
        return "Pair{" +
                "c=" + c +
                ", i=" + i +
                '}';
    }
}
class RandomPair{
    Random rand = new Random(47);
    // An infinite iterator of random capital letters:
    Iterator<Character> capChars = rand.ints(65,91) // 整数流 随机生成大些字母迭代器
            .mapToObj(i -> (char)i)//转化为Pair流
            .iterator();
    public Stream<Pair> stream(){
        return rand.ints(100,1000).distinct().mapToObj(i -> new Pair(capChars.next(),i));
    }
}

public class MapCollector {
    public static void main(String[] args) {
        Map<Integer,Character> map = new RandomPair().stream()
                .limit(8)
                .collect(Collectors.toMap(Pair::getI,Pair::getC));
        //只使用最简单形式的 Collectors.toMap(),这个方法值需要一个可以从流中获取键值对的函数。还有其他重载形式,其中一种形式是在遇到键值冲突时,需要一个函数来处理这种情况。
        System.out.println(map);
    }
}

package stream;

import java.io.IOException;
import java.util.ArrayList;

public class SpecialCollector {
    public static void main(String[] args) throws IOException {
        ArrayList<String> words = FileToWords.stream("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat")
                .collect(ArrayList::new,
                        ArrayList::add,
                        ArrayList::addAll);
        words.stream()
                .filter(s -> s.equals("cheese"))
                .forEach(System.out::println);
    }
}
/*
cheese
cheese
 */

5.4 组合

reduce(BinaryOperator):使用 BinaryOperator 来组合所有流中的元素。因为流可能为空,其返回值为 Optional。
reduce(identity, BinaryOperator):功能同上,但是使用 identity 作为其组合的初始值。因此如果流为空,identity 就是结果。
reduce(identity, BiFunction, BinaryOperator):更复杂的使用形式(暂不介绍),这里把它包含在内,因为它可以提高效率。通常,我们可以显式地组合 map() 和 reduce() 来更简单的表达它。

package stream;

import java.util.Random;
import java.util.stream.Stream;

class Frobnitz{
    int size;
    Frobnitz(int sz){size = sz;}

    @Override
    public String toString() {
        return "Frobnitz{" +
                "size=" + 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) // supply() 的生成器;因为这个方法对于 Supplier<Frobnitz> 是签名兼容的,我们可以将其方法引用传递给 Stream.generate()(这种签名兼容性被称作结构一致性)
                .limit(10)
                .peek(System.out::println)
                .reduce((fr0,fr1) -> fr0.size < 50 ? fr0 : fr1) //Lambda 表达式中的第一个参数 fr0 是上一次调用 reduce() 的结果。而第二个参数 fr1 是从流传递过来的值。
                .ifPresent(System.out::println);//Optional.ifPresent() 只有在结果非空的时候才会调用 Consumer<Frobnitz>
    }
}
/*
Frobnitz{size=58}
Frobnitz{size=55}
Frobnitz{size=93}
Frobnitz{size=61}
Frobnitz{size=61}
Frobnitz{size=29}
Frobnitz{size=68}
Frobnitz{size=0}
Frobnitz{size=22}
Frobnitz{size=7}
Frobnitz{size=29}
 */

5.5 匹配

allMatch(Predicate) :如果流的每个元素根据提供的 Predicate 都返回 true 时,结果返回为 true。在第一个 false 时,则停止执行计算。
anyMatch(Predicate):如果流中的任意一个元素根据提供的 Predicate 返回 true 时,结果返回为 true。在第一个 false 是停止执行计算。
noneMatch(Predicate):如果流的每个元素根据提供的 Predicate 都返回 false 时,结果返回为 true。在第一个 true 时停止执行计算。

package stream;

import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;

interface Matcher extends BiPredicate<Stream<Integer>, Predicate<Integer>>{} //BiPredicate 是一个二元谓词
public class Matching {
    static void show(Matcher match,int val){
        System.out.println(
                match.test(IntStream.rangeClosed(1,9)
                        .boxed()
                        .peek(n -> System.out.format("%d ",n))
                        ,n -> n < val));
    }

    public static void main(String[] args) {
        show(Stream::allMatch,10);
        show(Stream::allMatch,4);
        show(Stream::anyMatch,2);
        show(Stream::anyMatch,0);
        show(Stream::noneMatch,5);
        show(Stream::noneMatch,0);
    }
}
/*
1 2 3 4 5 6 7 8 9 true
1 2 3 4 false
1 true
1 2 3 4 5 6 7 8 9 false
1 false
1 2 3 4 5 6 7 8 9 true
 */

5.6 查找

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

package stream;
import static stream.RandInts.*;

public class SelectElement {
    public static void main(String[] args) {
        System.out.println(rands().findFirst().getAsInt());
        System.out.println(rands().parallel().findFirst().getAsInt());// findFirst() 无论流是否为并行化的,总是会选择流中的第一个元素
        System.out.println(rands().findAny().getAsInt());//非并行流,findAny()会选择流中的第一个元素(即使从定义上来看是选择任意元素)
        System.out.println(rands().parallel().findAny().getAsInt()); //使用 parallel() 来并行流从而引入 findAny() 选择非第一个流元素的可能性。
    }
}
/*
258
258
258
242
 */

package stream;

import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class LastElement {
    public static void main(String[] args) {
        OptionalInt last = IntStream.range(10,20).reduce((n1,n2)-> n2);
        System.out.println(last.orElse(-1));
        // Non-numeric object:
        Optional<String> lastobj = Stream.of("one","two","three").reduce((n1,n2) -> n2);
        System.out.println(lastobj.orElse("Nothing there!"));
    }
}
/*
19
three
 */

5.7 信息

count():流中的元素个数。
max(Comparator):根据所传入的 Comparator 所决定的“最大”元素。
min(Comparator):根据所传入的 Comparator 所决定的“最小”元素。

package stream;

import java.io.IOException;

public class Informational {
    public static void main(String[] args) throws IOException {
        System.out.println(FileToWords.stream("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat").count());
        System.out.println(FileToWords.stream("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat")
                .min(String.CASE_INSENSITIVE_ORDER) //min() 和 max() 的返回类型为 Optional,这需要我们使用 orElse()来解包
                .orElse("None")
        );
        System.out.println(FileToWords.stream("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/stream/Cheese.dat")
                .max(String.CASE_INSENSITIVE_ORDER)
                .orElse("None")
        );
    }
}
/*
32
a
you
 */

5.8 数字流信息

average() :求取流元素平均值。
max() 和 min():数值流操作无需 Comparator。
sum():对所有流元素进行求和。
summaryStatistics():生成可能有用的数据。目前并不太清楚这个方法存在的必要性,因为我们其实可以用更直接的方法获得需要的数据。

package stream;
import static stream.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}
 */

6 本章小结

学习参考 https://lingcoder.github.io/OnJava8/#/book/14-Streams?id=信息

原文地址:https://www.cnblogs.com/erinchen/p/12339880.html