点击链接可以查看: java8 lambda表达式使用详解
然而,除了lambda表达式外,还有一个stream流,也可以极大的提高编码效率,并且能够提高代码的可读性。lambda表达式主要用在实现函数接口的地方,而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 的 抽象方法如下,接收一个参数, 返回一个对象. 并且提供了一个返回自身对象的静态方法(常用)
R apply(T t);
static <T> Function<T, T> identity() {
return t -> t;
}
Consumer 的抽象方法如下, 接收一个参数, 没有返回值
void accept(T t);
Supplier 接口的方法不接收参数, 但返回一个对象
T get();
Predicate 接口接收一个参数, 返回boolean基本类型, 用于进行判断, 同时提供一个 isEqual 的静态方法(啊,才发现)
boolean test(T t);
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
看过上面的示例后,再来详细地介绍一些stream常用的api,
// Collection 接口中的stream方法
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
在使用 stream 时,都需要通过 stream() 或 parallelStream() 获取流式对象 Stream,实现流Collection接口的类都可以使用 stream,其中 parallelStream 是使用多线程进行流式处理。
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)
// 使用该无参方法,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));
}
<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()
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();
// 提供一个标识对象, 拿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 的介绍注释基本都写了, 示例就不写了, 可以参照前面的自己尝试, 然后有两点提一下,
empty()
, of(T t)
, ofNullable(T t)
三个静态方法得到, 分别表示null, 非空对象, 可以为null对象. 然后可以直接get得到其持有的对象, 或直接使用Optional 提供的方法对其进行操作.这里主要介绍
R collect(Supplier supplier,
BiConsumer accumulator,
BiConsumer combiner);
// Collector 相当于对 上面三个参数的封装
R collect(Collector super T, A, R> collector);
这个主要介绍第二个重载方法, 因为 Stream 提供了很多现成的 Collectors, 经常使用的如下
toList()
将数据流聚合成 ArrayListtoSet()
将数据流聚合成 HashSettoCollection(Supplier collectionFactory)
聚合成Collection, 自己提供cellection生成器 如LinkedList::new
groupingBy(Function super T, ? extends K> classifier)
根据classifier获取key, 生成 Map, List> 对象groupingBy(Function super T, ? extends K> classifier, Collector super T, A, D> downstream)
同上, 可以自行选择下游聚合方式 (即生成List, 还是Set 或者其他)toMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper)
跟据两个参数(获取key 和 value)生成 MaptoMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper, BinaryOperator mergeFunction)
同上, 当同一个key 存在多个value时, 使用 mergerFunction 进行合并(二选一)当然除了上面介绍的这些还有很多好用的api, 大家用到的时候可以去自行查看.