Java中Stream流详解

目录

今天在Java学习中,遇到了starm这个操作方式,了解后发现很多操作都很实用并且在项目开发中经常用到,特写下此篇博客用来记录。

一、Stream基础概念

Stream是Java 8中引入的全新API,可以极大地方便我们对集合、数组等数据源进行连续操作。它可以简化我们的代码,使代码更加易于维护和理解。Stream实际上是一种惰性计算的方式,只有需要输出结果时,才会开始计算。

Stream操作中的惰性计算

Stream只是对于原有数据的操作方式,数据本身并没有改变。因此,在对Stream进行操作时,实际上是将操作指令存储在操作流中并未计算执行,直到需要输出结果时才会被触发。这种方式可以减少计算量和开销,提高效率。

创建Stream

可以从很多种数据源中创建Stream,例如List、Set或者任何其他实现了Iterable接口的类。创建方式很简单,使用stream()parallelStream()方法即可。

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream(); // 串行Stream
Stream<String> parallelStream = list.parallelStream(); // 并行Stream

二、常用的Stream操作方法

常用的Stream操作包括:过滤、映射、排序、去重、计数、归约等等。

filter

过滤方法filter用于对Stream中的元素进行筛选,只保留符合指定条件的元素。其函数式接口为Predicate,其方法为boolean test(T t),接受一个T类型的对象,并返回一个boolean类型值。当该方法返回true时,说明该元素符合条件,将被保留在Stream中。

例如以下代码:

List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
Stream<String> stream = list.stream().filter(s -> s.length() > 5);

其中的lamdba表达式s -> s.length() > 5用于筛选长度大于5的字符串。

map

映射方法map用于将Stream中的元素根据指定规则进行转换。其函数式接口为Function,其方法为R apply(T t),接受一个T类型的对象,并返回一个R类型的对象。实质上,map方法就是对Stream中各元素做一个同类型的映射。

例如以下代码:

List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
Stream<Integer> stream = list.stream().map(String::length);

其中的方法引用String::length用于将每个字符串对象转换成其长度值。

sorted

排序方法sorted用于对Stream中的元素进行排序。其函数式接口为Comparator,其方法为int compare(T o1, T o2),接受两个T类型的对象,并返回一个int类型值。当返回值为负数时,说明o1应排在o2前面;当返回值为正数时,说明o1应排在o2后面;当返回值为0时,说明o1与o2的顺序不确定。

例如以下代码:

List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
Stream<String> stream = list.stream().sorted();

其中,Stream将使用默认的排序规则对元素进行排序。

distinct

去重方法distinct用于将Stream中的重复元素去除,只保留一个。其使用equals方法进行比较,因此需要保证数据源中的元素正确实现了equals方法。

例如以下代码:

List<String> list = Arrays.asList("apple", "banana", "orange", "banana");
Stream<String> stream = list.stream().distinct();

其中,Stream中的元素"banana"出现了两次,但在调用distinct方法后,只保留了一次的"banana"元素。

count

计数方法count用于返回Stream中元素的数量,返回值为long类型。

例如以下代码:

List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
long count = list.stream().count();

其中,count方法返回的值为4,即Stream中元素的数量。

reduce

归约方法reduce用于将Stream中的元素归约成一个值。其函数式接口为BinaryOperator,其方法为apply(T t1, T t2),用于对两个T类型值进行归约,返回一个T类型值。reduce方法接受两个参数:第一个参数表示归约操作的初始值,可以为任意类型的对象;第二个参数为一个BinaryOperator类型的对象,用于对Stream中所有元素递归地进行归约操作。

例如以下代码:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.stream().reduce(0, Integer::sum);

forEach

forEach方法用于对Stream中的每个元素执行指定的操作,其函数式接口为Consumer,其方法为void accept(T t)forEach是一个终端操作,对于同一个Stream只能进行一次,一旦执行了终端操作,该Stream就不能再重复使用了。

例如以下代码:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream().forEach(System.out::println);

其中,先通过stream方法将集合转换为Stream流,接着使用forEach方法对每个整数进行输出,最终输出结果为:

1
2
3
4
5

三、总结

Stream是Java 8中非常重要的一个API,可以极大地方便我们对列表、集合等数据源进行连续操作。Stream操作可以极大地简化我们的代码,提高效率,因此在Java编程中应该熟练使用Stream。需要注意的是,在对Stream进行操作时,应该注意Stream的惰性计算特性,以避免不必要的计算开销。