Updated on 2016-11-09

StringJoiner | Optional

https://docs.oracle.com/javase/10/docs/api/java/util/stream/Stream.html

https://docs.oracle.com/javase/10/docs/api/java/util/stream/Collectors.html

Lambda

  • Lambda 表达式(匿名方法 or 闭包):由 参数列表->函数体 组成。
    • 参数列表:可以省略参数类型,编译器会根据上下文推导。
    • 函数体:引用的局部变量会被隐式声明为 final。
      • 代码块:用 { } 包裹的多条执行代码。
      • 表达式:只有一条执行代码,省略了 { }return
        • 没有传入额外参数,而 仅调用对象方法,则可以进一步简化整个 Lambda 表达式,转换为 方法引用对象名::方法名 类名::方法名 类名::new 类名[]::new
  • Lambda 表达式只能出现在目标类型为函数式接口的上下文中。
  • Lambda 表达式不会引入新作用域,函数体中的变量和外部环境中的变量具有相同的语义。
Predicate<String> filter = (String s) -> {     代码块
    return s.endsWith(".txt");
};

Predicate<String> filter = (String s) -> s.endsWith(".txt");     表达式

Predicate<String> filter = s -> s.endsWith(".txt");     省略参数类型

-------------------------------------------------------

Runnable runnable = () -> {     代码块
    System.out.println("123");
};

Runnable runnable = () -> System.out.println("123");     表达式

Runnable runnable = System.out::println;     若去掉 "123"则可以转为方法引用没有传入额外参数

-------------------------------------------------------

Arrays.asList("A", "B", "C").sort((String s1, String s2) -> {     代码块
    return s1.compareTo(s2);
});

Arrays.asList("A", "B", "C").sort((String s1, String s2) -> s1.compareTo(s2));     表达式

Arrays.asList("A", "B", "C").sort((s1, s2) -> s1.compareTo(s2));     省略参数类型

Arrays.asList("A", "B", "C").sort(String::compareTo);     方法引用没有传入额外参数

-------------------------------------------------------

Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);     表达式

Comparator<Integer> comparator = Integer::compare;     方法引用没有传入额外参数

Functional Interface

只含有一个抽象方法,因此可以被转换成 Lambda 表达式。

  • 抽象方法:自己 未实现,子类必须实现。void a();
  • 默认方法:自己 已实现,子类可以重写。default void b() { }
  • 静态方法:自己 已实现,直接可以访问。static void c() { }
@FunctionalInterface     注解声明为函数式接口
public interface A {
    void a();     抽象方法

    default void b() {     默认方法
        System.out.println("默认方法");
    }

    static void c() {     静态方法
        System.out.println("静态方法");
    }
}

public class Test {
    public static void main(String[] args) {
        Runnable r = () -> System.out.println("抽象方法");     根据上下文推导出目标类型  Runnable
        A a = () -> System.out.println("抽象方法");     根据上下文推导出目标类型  A
        a.a();
        a.b();
        A.c();
    }
}
----
输出
抽象方法
默认方法
静态方法

Stream

支持串行处理和并行处理的 数据流

  • 外部迭代:通过 for-each 循环来处理数据;同时承担 做什么怎么做
  • 内部迭代:通过获得 Stream 来处理数据;只承担 做什么,由类库承担 怎么做
    • 中间操作(惰性求值):不会立即执行,而是加入任务队列中,在终点操作时一并执行。
      • 有限数据 ➜ 优化作用,无限数据 ➜ 决定作用(没有短路求值,操作无法停止)。
    • 终点操作(急性求值):通过短路求值的优化一并执行所有操作,以提供更高效的性能。
  • 串行流(串行处理):单线程。list.stream() stream.sequential()
  • 并行流(并行处理):多线程。list.parallelStream() stream.parallel()
  • 中间操作:
    • 过滤:filter。
    • 排序:sorted。
    • 去重:distinct。
    • 最大:max。
    • 最小:min。
    • 消耗:peek。
    • 转换:map(一对一)、flatMap(一对多)。映射
    • 返回前 n 个元素:limit。最多执行次数
    • 跳过前 n 个元素:skip。
  • 终点操作:
    • 收集:collect。
    • 归约:reduce。
    • 消耗:forEach。
    • 且(全部 元素符合返回 true):allMatch。
    • 或(任一 元素符合返回 true):anyMatch。
    • 非(没有 元素符合返回 true):noneMatch。
for (Shape shape : shapes){     外部迭代
    shape.setColor(RED);
}

----

shapes.forEach(s -> s.setColor(RED));     内部迭代

-------------------------------------------------------

int[] array = IntStream.range(0, 3).toArray();     生成数组 [0, 1, 2]

IntStream.range(0, 3).forEach(System.out::println);     替代 for 循环
----
输出
0
1
2

-------------------------------------------------------

Stream
        .of("A", "B", "C")
        .filter(s -> {
            System.out.println("filter: " + s);
            return true;
        })
        .map(s -> {
            System.out.println("map: " + s);
            return s;
        });     没有调用终点操作
----
输出
----

Stream
        .of("A", "B", "C")
        .filter(s -> {
            System.out.println("filter: " + s);
            return true;
        })
        .map(s -> {
            System.out.println("map: " + s);
            return s;
        })
        .anyMatch(s -> {     调用终点操作
            System.out.println("anyMatch: " + s);
            return s.startsWith("B");
        });
----
输出     可以看出对元素的处理操作是垂直执行的像在流水线依次经过每个操作并通过短路求值尽可能减少操作次数
filter: A
map: A
anyMatch: A
filter: B
map: B
anyMatch: B

Notefilter 操作尽可能排在最前短路求值),sorted 操作尽可能排在最后sorted 操作遍历整个数据流

Optional

一种用于封装对象的容器,封装的对象可以为空,并且对空值进行处理。

public class A {
    public static void main(String[] args) {
        a();
        System.out.println("--------");
        b();
    }

    private static void a() {
        String s = "ABC";
        Optional<String> optional = Optional.ofNullable(s);     对象不为空

        System.out.println(optional.isPresent());
        System.out.println(optional.orElse("123"));     接收一个缺省值
        System.out.println(optional.orElseGet(() -> "123"));     接收一个 Supplier 函数式接口
    }

    private static void b() {
        String s = null;
        Optional<String> optional = Optional.ofNullable(s);     对象为空

        System.out.println(optional.isPresent());
        System.out.println(optional.orElse("123"));
        System.out.println(optional.orElseGet(() -> "123"));
    }
}
----
输出
true
ABC
ABC
--------
false
123
123

Code

public class A {
    private static final List<Author> LIST = Arrays.asList(     作家列表
            new Author("Adam", 23, Arrays.asList("Java1", "Java2")),
            new Author("Bell", 19, Arrays.asList("Python1", "Python2")),
            new Author("Conan", 23, Arrays.asList("PHP1", "PHP2")),
            new Author("David", 26, Arrays.asList("Ruby1", "Ruby2")));

    public static void main(String[] args) {
        Supplier<Stream<Author>> supplier = () -> LIST.stream().onClose(() -> System.out.println("——————————————"));     供应器
        a(supplier);                                                                                    Stream 关闭时回调 Runnable 对象
        b(supplier);
        c(supplier);
        d();
    }

    private static void a(Supplier<Stream<Author>> supplier) {     collect
        try (Stream<Author> stream = supplier.get()) {
            Set<Author> set = stream.filter(o -> o.mName.startsWith("B")).collect(Collectors.toSet());     获得以 B 开头的姓名toSet
            System.out.println(set);
        }
        try (Stream<Author> stream = supplier.get()) {
            List<Integer> list = stream.map(o -> o.mAge).distinct().sorted().collect(Collectors.toList());     有哪些年龄toList
            System.out.println(list);
        }
        try (Stream<Author> stream = supplier.get()) {
            Double aDouble = stream.collect(Collectors.averagingInt(o -> o.mAge));     获得平均年龄
            System.out.println(aDouble);
        }
        try (Stream<Author> stream = supplier.get()) {
            Map<Boolean, List<Author>> map = stream.collect(Collectors.partitioningBy(o -> o.mAge > 20));     按年龄一分为二
            map.forEach((key, value) -> System.out.printf("%s: %s\n", key, value));
        }
        try (Stream<Author> stream = supplier.get()) {
            Map<Integer, List<Author>> map = stream.collect(Collectors.groupingBy(o -> o.mAge));     按年龄分组toMap未提供收集器默认调用 toList()
            map.forEach((key, value) -> System.out.printf("%s: %s\n", key, value));
        }
        try (Stream<Author> stream = supplier.get()) {
            Map<Integer, Integer> map = stream.collect(Collectors.groupingBy(o -> o.mAge, Collectors.summingInt(o -> 1)));     按年龄分组toMap提供收集器指定收集行为
            map.forEach((key, value) -> System.out.printf("%s: %s\n", key, value));
        }
        try (Stream<Author> stream = supplier.get()) {
            String s = stream.filter(o -> o.mAge > 20).map(o -> o.mName).collect(Collectors.joining(", ", "<", ">"));     连接作家名字
            System.out.println(s);
        }
        try (Stream<Author> stream = supplier.get()) {
            Collector<Object, StringJoiner, String> myCollector = Collector.of(     自定义收集操作
                    () -> new StringJoiner(", ", "<", ">"),     supplier 供应器(开始)
                    ((stringJoiner, o) -> stringJoiner.add(o.toString())),     accumulator 累加器
                    (stringJoiner, stringJoiner2) -> stringJoiner.merge(stringJoiner2),     combiner 组合器 (并行流使用)
                    stringJoiner -> stringJoiner.toString());     finisher 终止器(结束)

            String s = stream.filter(o -> o.mAge > 20).map(o -> o.mName).collect(myCollector);     连接作家名字
            System.out.println(s);
        }
        ----
        输出
        [Bell]
        ——————————————————————————
        [19, 23, 26]
        ——————————————————————————
        22.75
        ——————————————————————————
        false: [Bell]
        true: [Adam, Conan, David]
        ——————————————————————————
        19: [Bell]
        23: [Adam, Conan]
        26: [David]
        ——————————————————————————
        19: 1
        23: 2
        26: 1
        ——————————————————————————
        <Adam, Conan, David>
        ——————————————————————————
        <Adam, Conan, David>
        ——————————————————————————
    }

    private static void b(Supplier<Stream<Author>> supplier) {     reduce
        try (Stream<Author> stream = supplier.get()) {
            Optional<Author> optional = stream.reduce((o1, o2) -> o1.mAge > o2.mAge ? o1 : o2);     accumulator 累加器获得最大年龄作家不提供起始值返回 Optional
            optional.ifPresent(o -> System.out.println(o.mName + "_" + o.mAge));
        }
        try (Stream<Author> stream = supplier.get()) {
            Author author = stream.reduce(
                    new Author("", 0, new ArrayList<>()),     提供起始值
                    (o1, o2) -> o1.mAge > o2.mAge ? o1 : o2);     accumulator 累加器获得最大年龄作家
            System.out.println(author.mName + "_" + author.mAge);
        }
        try (Stream<Author> stream = supplier.get()) {
            Integer integer = stream.reduce(     自定义归约操作
                    0,     起始值
                    (sum, o) -> sum += o.mAge,     accumulator 累加器获得年龄总和
                    (sum, sum2) -> sum + sum2);     combiner 组合器 (并行流使用)
            System.out.println(integer);
        }
        ----
        输出
        David_26
        ——————————————————————————
        David_26
        ——————————————————————————
        91
        ——————————————————————————
    }

    private static void c(Supplier<Stream<Author>> supplier) {     flatMap
        try (Stream<Author> stream = supplier.get()) {
            String s = stream.flatMap(o -> o.mArticle.stream()).collect(Collectors.joining(", ", "<", ">"));     连接所有作家中的所有文章
            System.out.println(s);
        }
        ----
        输出
        <Java1, Java2, Python1, Python2, PHP1, PHP2, Ruby1, Ruby2>
        ——————————————————————————
    }

    private static void d() {
        Stream.generate(UUID::randomUUID).limit(5).forEach(System.out::println);
        ----              无限生成 UUID限制最多执行 5 次
        输出
        e9100622-3458-4c94-bc7a-dabcf99ebee9
        bc18a526-945f-4fc6-a9fc-496c075aa99a
        57a3882b-2eb6-4392-bb2f-31cb10acbece
        82900c8e-e8d6-46de-a02f-9d37c5454f30
        25ec7ee2-a963-43df-8c11-7008b4a68ccd

        Stream.iterate(0, n -> n + 2).limit(10).forEach(n -> System.out.print(n + " "));
        ----            无限迭代起始值限制最多执行 10 次
        输出
        0 2 4 6 8 10 12 14 16 18

        List<String> list1 = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
        List<String> list2 = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
        Stream.of(list1, list2).peek(list -> list.addAll(Arrays.asList("E", "F", "J"))).forEach(System.out::println);     peek
        ----
        输出
        [A, B, C, D, E, F, J]
        [A, B, C, D, E, F, J]
    }

    private static class Author {        作家
        private String mName;            姓名
        private int mAge;                年龄
        private List<String> mArticle;   文章列表

        private Author(String name, int age, List<String> article) {
            mName = name;
            mAge = age;
            mArticle = article;
        }

        @Override
        public String toString() {
            return mName;
        }
    }
}