java8 四大函数式接口 和 用于数据处理的 stream流 使用详解

文章目录

      • Stream 使用示例
      • 四大函数式接口
        • Function 功能型接口
        • Consumer 消费型接口
        • Supplier 提供型接口
        • Predicate 断言型接口
      • 常用 api
        • stream,parallelStream 获取流式对象
        • filter 过滤
        • sorted 排序
        • map 映射,转换
        • peek 遍历执行某个操作
        • reduce 聚合,将数据流聚合成一个对象
        • collect 可变聚合, 可以聚合成一个集合

在之前一篇博客里面介绍了 java8 中的 lambda 表达式,使用lambda表达式可以大大的简化我们的代码,使代码更加简洁、优雅,敲代码的效率也更高。

点击链接可以查看: java8 lambda表达式使用详解

然而,除了lambda表达式外,还有一个stream流,也可以极大的提高编码效率,并且能够提高代码的可读性。lambda表达式主要用在实现函数接口的地方,而stream流则主要用于处理数据。以下为相关功能介绍。

Stream 使用示例

首先给一个示例,(结合lambda使用,效率翻倍)

@Data
public class Student {
    private Integer id;
    private String major;
    private String name;
    private Integer level;
    private Grade grade; // 成绩
}
// 假装里面有很多students对象
List<Student> students = new ArrayList<>();

// 仅做示例, 不要纠结逻辑
Map<Integer, Grade> names = students.stream()		// 获取stream流
	.filter(Objects::nonNull)		// 过滤为null的, lambda基础版 o -> Objects.nonNull(o)
	.distinct()			//去重, 调用equals方法判断
	.sorted(Comparator.comparingInt(Student::getId))		// 排序, 根据id升序排列
    .map(Student::getGrade) 		// 映射, 由 student 转化成 Grade
    .collect(Collectors.toMap(Grade::getName, Function.identity(), 
    	BinaryOperator.maxBy(Comparator.comparing(Grade::getScore)));		// 最后转成map

四大函数式接口

由于 stream 流中经常用到 java8 中四大函数式接口, 并且这四个接口还衍生出了很多其他的接口. 可能有一些小伙伴对这几个基本的接口不太了解, 所以这里就简单介绍一下.

Function 功能型接口

Function 的 抽象方法如下,接收一个参数, 返回一个对象. 并且提供了一个返回自身对象的静态方法(常用)

R apply(T t);
 
static <T> Function<T, T> identity() {
    return t -> t;
}
Consumer 消费型接口

Consumer 的抽象方法如下, 接收一个参数, 没有返回值

void accept(T t);
Supplier 提供型接口

Supplier 接口的方法不接收参数, 但返回一个对象

 T get();
Predicate 断言型接口

Predicate 接口接收一个参数, 返回boolean基本类型, 用于进行判断, 同时提供一个 isEqual 的静态方法(啊,才发现)

boolean test(T t);

static <T> Predicate<T> isEqual(Object targetRef) {
	return (null == targetRef)
		? Objects::isNull
		: object -> targetRef.equals(object);
}

常用 api

看过上面的示例后,再来详细地介绍一些stream常用的api,

stream,parallelStream 获取流式对象
// Collection 接口中的stream方法
default Stream<E> stream() {
	return StreamSupport.stream(spliterator(), false);
}

在使用 stream 时,都需要通过 stream() 或 parallelStream() 获取流式对象 Stream,实现流Collection接口的类都可以使用 stream,其中 parallelStream 是使用多线程进行流式处理。

filter 过滤
Stream<T> filter(Predicate<? super T> predicate);

该方法是用于对数据流进行过滤,传入一个 Predicate (四大函数式接口之一,断言接口)实例。使用Predicate 只需要实现一个 boolean test(T t) 方法就可以。对于返回为true的通过,返回false的丢弃。

使用示例:

List<Student> stus = new ArrayList<>();
// 非null的通过
stus = stus.stream().filter(Objects::nonNUll)
			.collect(Collectors.toList());	
// 相当于  s -> Objects.nonNull(s)
sorted 排序
// 使用该无参方法,T必须实现Comparable接口,否则会抛出类型转换异常
Stream<T> sorted();

Stream<T> sorted(Comparator<? super T> comparator);

sorted 方法用于对数据流进行排序,且为升序,提供了两个重载方法,一个无参的方法,数据流必须实现了Comparable接口,通过对应的compare方法进行比较;而另一个方法需要传入Comparator实例,自己提供用于排序比较的方法。

使用示例

List<Student> students = new ArrayList<>();
students = students.stream()
	// 根据 int 类型的 student.id进行排序
	.sorted(Comparator.comparingInt(Student::getId)) 
	.collect(Collectors.toList());

// comparingInt 的源码
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
   Objects.requireNonNull(keyExtractor);
   return (Comparator<T> & Serializable)
       (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}
map 映射,转换
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

map方法用于将当前类型转换为另一种类型,只需要传入一个 Function 接口,实现R apply(T t) 方法,即传入 T 类型返回 R类型的对象。同时还提供的 mapToInt、mapToLong、mapToDouble 等特定转换的方法。

使用示例

List<Student> students = new ArrayList<>();

Set<String> majorSet = students.stream()
	// 将student对象转转成String, 得到major的set集合
	.map(Student::getMajor).collect(Collectors.toSet());
	// s -> s.getMajor()
peek 遍历执行某个操作
Stream<T> peek(Consumer<? super T> action);

该方法有点类似于 forEach 方法,但与forEach不同的是,forEach 会直接终止数据流,而peek还能继续执行下去。提供一个 Consumer 参数,对数据流进行某个操作。

List<Student> students = new ArrayList<>();
long count = students.stream().filter(Objects::nonNull)
	// 打印每个学生的名字
	.peek(s -> System.out.println(s.getName()))
	// 最后统计数量
	.count();
reduce 聚合,将数据流聚合成一个对象
// 提供一个标识对象, 拿identity与数据流进行聚合, 然后返回结果, 数据流空就返回identity
T reduce(T identity, BinaryOperator<T> accumulator);

// 没有标识对象, 直接在数据流内进行聚合, 可能不存在结果(数据流空的), 但避免返回null, 因此使用Optional对象
Optional<T> reduce(BinaryOperator<T> accumulator);

// 返回另一种类型的对象, 提供标识, accumulator 用上一个结果U与下一个数据T进行聚合返回U, combiner 将两个聚合结果进行合并
<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

reduce 的介绍注释基本都写了, 示例就不写了, 可以参照前面的自己尝试, 然后有两点提一下,

  • Optional 也是java8 新增的一个对象, 可以用于避免NPE(nullPointException) 问题, Optional 对象可以通过 empty(), of(T t), ofNullable(T t) 三个静态方法得到, 分别表示null, 非空对象, 可以为null对象. 然后可以直接get得到其持有的对象, 或直接使用Optional 提供的方法对其进行操作.
  • reduce 的第三种重载方法, 第三个参数, 是在并发场景下才会用到, 因为并发流使用的 fork/join 框架, 将数据流进行分割(类似二分法), 然后计算, 所以会得到多个结果, 最后需要对这些结果进行合并.
collect 可变聚合, 可以聚合成一个集合

这里主要介绍

 R collect(Supplier supplier,
                  BiConsumer accumulator,
                  BiConsumer combiner);

// Collector 相当于对 上面三个参数的封装
 R collect(Collector collector);

这个主要介绍第二个重载方法, 因为 Stream 提供了很多现成的 Collectors, 经常使用的如下

  • toList() 将数据流聚合成 ArrayList
  • toSet() 将数据流聚合成 HashSet
  • toCollection(Supplier collectionFactory) 聚合成Collection, 自己提供cellection生成器 如LinkedList::new
  • groupingBy(Function classifier) 根据classifier获取key, 生成 Map 对象
  • groupingBy(Function classifier, Collector downstream) 同上, 可以自行选择下游聚合方式 (即生成List, 还是Set 或者其他)
  • toMap(Function keyMapper, Function valueMapper) 跟据两个参数(获取key 和 value)生成 Map
  • toMap(Function keyMapper, Function valueMapper, BinaryOperator mergeFunction) 同上, 当同一个key 存在多个value时, 使用 mergerFunction 进行合并(二选一)

当然除了上面介绍的这些还有很多好用的api, 大家用到的时候可以去自行查看.

你可能感兴趣的:(Java,java,lambda,stream,函数式编程)