JAVA学习笔记(三)- 函数式编程

205 阅读3分钟

函数式编程

  • 函数式编程(Functional Programming)是把函数作为基本运算单元,函数可以作为变量可以接收函数还可以返回函数。(参考js匿名函数)

历史上研究函数式编程的理论是Lambda演算,所以我们经常把支持函数式编程的编码风格称为Lambda表达式。

Lambda基础

(s1, s2) -> {
    return s1.compareTo(s2);
}
Arrays.sort(array, (s1, s2) -> s1.compareTo(s2));

Tip

  • 单方法接口被称为FunctionalInterface。
  • 接收FunctionalInterface作为参数的时候,可以把实例化的匿名类改写为Lambda表达式,能大大简化代码。

方法引用

  • 方法引用,指如果某个方法签名和接口恰好一致,就可以直接传入方法引用。

  • 方法签名相同:即方法参数一致,返回类型相同

// 传入class Main的cmp方法
Arrays.sort(array, Main::cmp);

Tip

注意,实例方法有一个隐含的this参数

构造方法引用

public class Main {
    public static void main(String[] args) {
        List<String> names = List.of("Bob", "Alice", "Tim");
        // 先转换为steam 然后引用Person构造方法,collect成List
        List<Person> persons = names.stream().map(Person::new).collect(Collectors.toList());
        System.out.println(persons);
    }
}

Tip

  • 构造方法隐士返回this实例
  • 构造方法引用写法时 类名::new

使用Stream

java.iojava.util.stream
存储顺序读写的byte或char顺序输出的任意Java对象实例
用途序列化至文件或网络内存计算/业务逻辑
  • 特点:流式处理的抽象序列 / 支持函数式编程和链式操作

创建Stream

  • Stream.of()
Stream<String> stream = Stream.of("A", "B", "C", "D");
  • 基于数组或Collection
Stream<String> stream1 = Arrays.stream(new String[] { "A", "B", "C" });
Stream<String> stream2 = List.of("X", "Y", "Z").stream();

Tip

Collection指(List、Set、Queue等)

  • 基于Supplier
Stream<String> s = Stream.generate(Supplier<String> sp);
  • 其他(Files类的lines()方法/pattern对象的splitAsStream())
try (Stream<String> lines = Files.lines(Paths.get("/path/to/file.txt"))) {
    ...
}
Pattern p = Pattern.compile("\\s+");
Stream<String> s = p.splitAsStream("The quick brown fox jumps over the lazy dog");
s.forEach(System.out::println);
  • 基本类型 IntStream、LongStream和DoubleStream。
// 将int[]数组变为IntStream:
IntStream is = Arrays.stream(new int[] { 1, 2, 3 });
// 将Stream<String>转换为LongStream:
LongStream ls = List.of("1", "2", "3").stream().mapToLong(Long::parseLong);

常用方法

map / filter / reduce : 类似 js的同名方法

tip

注意reduce reduce 返回类型是Optional对象, 需要进一步判断是否存在(例如 0个元素的情况)

Optional<Integer> opt = stream.reduce((acc, n) -> acc + n);
if (opt.isPresent()) {
    System.out.println(opt.get());
}
public class Main {
    public static void main(String[] args) {
        // 设置初始值 为 1 
        int s = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(1, (acc, n) -> acc * n);
        System.out.println(s); // 362880
    }
}

输出集合

  • 输出为List: collect()方法并传入Collectors.toList()
stream.filter(s -> s != null && !s.isBlank()).collect(Collectors.toList());
  • 输出为数组 - toArray()方法
List<String> list = List.of("Apple", "Banana", "Orange");
String[] array = list.stream().toArray(String[]::new);
  • 输出为map collect()方法并传入Collectors.toMap()
Stream<String> stream = Stream.of("APPL:Apple", "MSFT:Microsoft");
        Map<String, String> map = stream
                .collect(Collectors.toMap(
                        // 把元素s映射为key:
                        s -> s.substring(0, s.indexOf(':')),
                        // 把元素s映射为value:
                        s -> s.substring(s.indexOf(':') + 1)));
  • 分组输出 collect 方法传入 Collectors.groupingBy()
public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("Apple", "Banana", "Blackberry", "Coconut", "Avocado", "Cherry", "Apricots");
        Map<String, List<String>> groups = list.stream()
                .collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.toList()));
        System.out.println(groups);
    }
}

Tip groupingBy需要提供两个函数:

  • 一个是分组的key,这里使用s -> s.substring(0, 1),表示只要首字母相同的String分到一组,
  • 第二个是分组的value,这里直接使用Collectors.toList(),表示输出为List

其他操作

  • Stream提供的常用操作有:

  • 转换操作:map(),filter(),sorted(),distinct();

  • 合并操作:concat(),flatMap();

  • 并行处理:parallel();

  • 聚合操作:reduce(),collect(),count(),max(),min(),sum(),average();

  • 其他操作:allMatch(), anyMatch(), forEach()。


  • sorted()
public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("Orange", "apple", "Banana")
            .stream()
            .sorted()
            .collect(Collectors.toList());
        System.out.println(list);
    }
}
List<String> list = List.of("Orange", "apple", "Banana")
    .stream()
    .sorted(String::compareToIgnoreCase)
    .collect(Collectors.toList());

Tip

要求Stream的每个元素必须实现Comparable接口

  • 去重distinct()
List.of("A", "B", "A", "C", "B", "D")
    .stream()
    .distinct()
    .collect(Collectors.toList()); // [A, B, C, D]
  • 截取skip()/limit()
List.of("A", "B", "C", "D", "E", "F")
    .stream()
    .skip(2) // 跳过A, B
    .limit(3) // 截取C, D, E
    .collect(Collectors.toList()); // [C, D, E]
  • 合并concat(),
Stream<String> s1 = List.of("A", "B", "C").stream();
Stream<String> s2 = List.of("D", "E").stream();
// 合并:
Stream<String> s = Stream.concat(s1, s2);
System.out.println(s.collect(Collectors.toList())); // [A, B, C, D, E]
  • flatMap()
  • 并行parallel()
Stream<String> s = ...
String[] result = s.parallel() // 变成一个可以并行处理的Stream
                   .sorted() // 可以进行并行排序
                   .toArray(String[]::new);