前言
Stream流自从随着JDK1.8出来后一直褒贬不一,有的人觉得提高了Java编程效率,消除很多冗余的代码。但是也有一部分人觉得,流式编程虽然爽,但是代码可读性和维护性大大降低了很多。
先说下,我个人的看法是支持前者的。后者说到的影响可读性和维护性确实是存在的,但是这其实是因为个人编码不够规范,没有遵循相关的约定去编写流式代码,造成又臭又长难以维护的代码。
探究下Stream源码
public interface Stream<T> extends BaseStream<T, Stream<T>> {
//过滤筛选出匹配的元素,`Predicate`是`断言`返回一个布尔值
//`Predicate`后续会专门对这个写篇文章介绍
Stream<T> filter(Predicate<? super T> predicate);
//入参是一个函数,出参是一个新的流对象
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
//下面这几个应该一看就懂了,不多做解释
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
//入参是一个流,出参是一个新的流对象 (和`map`区别是`flatMap`是将两个流合并成一个新的流)
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
//下面几个也是一看就懂不多做解释
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
//排序
Stream<T> sorted();
//排序 入参是 `Comparator`
Stream<T> sorted(Comparator<? super T> comparator);
Stream<T> peek(Consumer<? super T> action);
//限制返回的元素数量 maxSize
Stream<T> limit(long maxSize);
//跳过前面 n 个元素,常常配合 `limit` 使用实现分页
Stream<T> skip(long n);
//循环遍历
void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);
//转数组
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
//可以做一些累加连续的操作 (具体可看后续的示例)
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
//元素收集
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
//最小值
Optional<T> min(Comparator<? super T> comparator);
//最大值
Optional<T> max(Comparator<? super T> comparator);
//统计数量
long count();
//下面几个也不多解释了,见名知意
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
Optional<T> findFirst();
Optional<T> findAny();
}
介绍下常用的几个函数
先定义一个Demo类
@Data
@Accessors(chain = true)
public class DemoEntity implements Serializable {
private static final long serialVersionUID = 4614169948081019080L;
private String userName;
private Integer age;
private Double money;
private List<Integer> number;
private int[] ints;
private double[] doubles;
}
map
map是常用的一个方法,将数组按规则转化成一个新的元素流。
//入参是一个函数( 即`DemoEntity.getAge()` ),获取对象中的`age`属性,生成一个age对象流
List<Integer> newList = list.stream().map(DemoEntity::getAge).collect(Collectors.toList());
flatMap
flatMap也是常用的方法,用法和map类似,区别是它是将两个流合并成一个新的流.
List<Integer> newList2 = list.stream().flatMap(po -> po.getNumber().stream()).collect(Collectors.toList());
distinct
distinct 基于对象equal方法进行元素去重,如果特殊需求,需要重写equal。
//distinct 通过equals()去重复
List<DemoEntity> disList = list.stream().distinct().collect(Collectors.toList());
sorted
sorted用于排序,可通过Comparator设置排序规则
//自然排序
list.stream().sorted();
//指定排序 .reversed() 可实现倒序
list.stream().sorted(Comparator.comparing(DemoEntity::getAge).reversed()).collect(Collectors.toList());
toArray
toArray用于将流转为数组
list.stream().toArray();
reduce
reduce可以做一些累加连续操作,第一个参数是累加初始值,第二个是Accumulator函数(第一个参数是上个归并函数的返回值,第二个是Stream中下一个元素)
//累加操作基本类型,下面是简单写法
List<Integer> numbers = Arrays.asList(1, 2, 3);
int total1 = numbers.stream().reduce(0, (total, number) -> total + number);
int total2 = numbers.stream().reduce(0, Integer::sum);
//累加操作实体对象,需要后面声明下
list.stream().reduce(0, (total, entity) -> total + entity.getAge(), Integer::sum);
list.stream().reduce("", (total, entity) -> total + entity.getUserName(), String::concat);
limit&skip
limit用于限制返回条数,skip用于跳过元素
//跳过元素10,限制元素10
list.stream().skip(10).limit(10);
小牛试刀一下
假设有个场景: 需要统计学生考试的 最高分,最低分, 大于60分人数
定义一个学生对象类
/**
* 学生对象
*
* @author: Mock
* @date: 2023-05-28 20:21:23
*/
@Data
@AllArgsConstructor
public class Student {
private String name;
private Integer score;
}
测试类
/**
* 测试类
*
* @author: Mock
* @date: 2023-05-28 20:22:24
*/
@Data
public class StreamTest {
private final static List<Student> students =
Lists.newArrayList(
new Student("张三", 67),
new Student("李四", 77),
new Student("王五", 83),
new Student("赵六", 91)
);
public static void main(String[] args) {
System.out.println(
"最高分: " +
students.stream().max(Comparator.comparing(Student::getScore))
.get().getScore()
);
System.out.println(
"最低分: " +
students.stream().min(Comparator.comparing(Student::getScore))
.get().getScore()
);
System.out.println(
"大于60分人数: " +
students.stream().filter(student -> student.getScore() >= 60)
.count()
);
}
}
运行结果