一、简介
JDK 8 引入了 Stream API,它是用于处理集合数据的功能强大的库。Stream API 提供了一种更为简洁、灵活和函数式的方式来进行集合的操作和处理。
Stream API 有三大特性:
- 不存储数据:Stream API 并不会在内存中存储数据,它仅仅是对源数据进行操作和处理的管道,当我们对一个集合或数组创建流时,流只是作为一种处理方式存在,并没有实际保存数据。
- 不改变源数据:Stream API 的操作不会改变原始数据源中的元素,所有的中间操作(如过滤、映射、排序)都会产生一个新的流,而不是直接修改原始数据。这种特性确保了数据的不可变性。
- 延时执行:Stream API 使用了延迟执行的概念。它并不会立即执行流的操作,而是等到需要结果时才进行计算。这样可以避免不必要的计算,提高效率并节省资源。只有在终端操作(如聚合、收集、计数)被调用时,流才会进行实际的计算。
二、操作分类
在使用 Stream API 进行集合操作时,一般会遵循以下步骤:
- 创建流:通过集合或数组创建一个流
- 中间操作:对流进行一系列的中间操作,例如过滤、映射、排序等。这些操作可以按照需求进行链式调用
- 结束操作:中间操作只是一种标记,只有结束操作才会触发实际计算
其中中间操作又分为,无状态和有状态,结束操作又分为短路操作和非短路操作
创建流
通过集合
可以使用 Collection
接口中的 stream()
方法或者 parallelStream()
方法来创建一个流
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
通过数组
可以使用 Arrays
类中的静态方法 stream()
来创建一个数组的流
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
通过Stream的of()方法
可以使用 Stream
类中的静态方法 of()
来根据指定的元素创建一个流
Stream<String> stream = Stream.of("a", "b", "c");
中间操作
无状态
元素的处理不受前一个元素影响
- filter(过滤):接收一个
Predicate
参数,根据Predicate
的判断结果决定是否保留流中的元素,true
留下,false
丢弃 - map、mapToInt、mapToLong、mapToDouble(转换):map 方法接收一个
Function
参数,将流中的每个元素通过该函数进行转换,mapToInt、mapToLong、mapToDouble 和 map 差不多,只是强制指定了返回值必须是 int、long、double 类型 - flatMap、flatMapToInt、flatMapToLong、flatMapToDouble(合并):将一个或多个流合并成一个新流,flatMapToInt、flatMapToLong、flatMapToDouble 和 f latMap 差不多,只是返回的是对应的 IntStream、LongStream、DoubleStream 流
- peek(监测):接受一个
Consumer
函数作为参数,该函数会在流的每个元素被处理时被调用。它可以用于在处理流的过程中观察每个元素的值,而不会改变流的内容
//filter
public static void main(String[] args) {
String[] str = {"a", "b", "c"};
Stream.of(str).filter(t -> t.equals("a")).forEach(System.out::println);
//输出
//a
}
//map、mapToInt、mapToLong、mapToDouble
public static void main(String[] args) {
String[] str = {"a", "b", "c"};
Stream.of(str).map(t -> t.toUpperCase()).forEach(System.out::println);
Stream.of(str).mapToInt(t -> t.hashCode()).forEach(System.out::println);
//输出
//A
//B
//C
//97
//98
//99
}
//flatMap、flatMapToInt、flatMapToLong、flatMapToDouble
public static void main(String[] args) {
List<String> a = new ArrayList<>();
a.add("a");
a.add("b");
List<String> b = new ArrayList<>();
b.add("c");
b.add("d");
Stream.of(a, b).flatMap(u -> u.stream()).forEach(System.out::println);
//输出
//a
//b
//c
//d
}
//peek
public static void main(String[] args) {
String[] str = {"a", "b", "c"};
Stream.of(str).peek(t -> System.out.println("this is " + t)).collect(Collectors.toList());
//输出
//this is a
//this is b
//this is c
}
有状态
必须等所有元素处理完毕之后才知道最终的结果
- distinct(去重):去除重复的元素
- sorted(排序):不传参数,会按照自然排序,也可以传一个比较器参数,会根据比较器定义的顺序排序
- limit(限制):截取前n个元素
- skip(跳过):跳过n个元素,返回之后的元素
//distinct
public static void main(String[] args) {
String[] str = {"a", "a", "b", "b", "c"};
Stream.of(str).distinct().forEach(System.out::println);
//输出
//a
//b
//c
}
//sorted
public static void main(String[] args) {
String[] str = {"banana", "apple", "pineapple", "pear", "watermelon"};
Stream.of(str).sorted().forEach(System.out::println);
System.out.println("---------------");
Stream.of(str).sorted(Comparator.comparing(String::length)).forEach(System.out::println);
//输出
//apple
//banana
//pear
//pineapple
//watermelon
//---------------
//pear
//apple
//banana
//pineapple
//watermelon
}
//limit
public static void main(String[] args) {
String[] str = {"a", "b", "c", "d", "e"};
Stream.of(str).limit(2).forEach(System.out::println);
//输出
//a
//b
}
//skip
public static void main(String[] args) {
String[] str = {"a", "b", "c", "d", "e"};
Stream.of(str).skip(2).forEach(System.out::println);
//输出
//c
//d
//e
}
结束操作
非短路操作
需要处理完所有元素
- forEach(循环):循环操作Stream中数据
- forEachOrdered(排序循环):按照流的遭遇顺序来处理元素,在并行流中使用
- toArray(转数组):不传参数的话,返回的是对象数组,也可以接收一个 IntFunction<A[]> generator 参数来指定返回数据的类型
- reduce(聚合):聚合操作,一般用来做统计
- collect(收集):将元素收集到一个集合或其他数据结构里
- min(最小值):根据传入的比较器,找到最小的元素
- max(最大值):根据传入的比较器,找到最大的元素
- count(总数量):计数,统计元素数量
//forEach
public static void main(String[] args) {
String[] str = {"a", "b", "c"};
Stream.of(str).forEach(System.out::println);
//输出
//a
//b
//c
}
//toArray
public static void main(String[] args) {
String[] str = {"1", "2", "3"};
Object[] objectArray = Stream.of(str).toArray();
String[] strArray = Stream.of(str).toArray(String[]::new);
}
//reduce
public static void main(String[] args) {
Integer[] array = {1, 2, 3, 4, 5};
Optional<Integer> optional = Stream.of(array).reduce((x, y) -> x + y);
optional.ifPresent(System.out::println);
//输出
//15
}
//collect
public static void main(String[] args) {
Integer[] array = {1, 2, 3, 4, 5};
List<Integer> list = Stream.of(array).collect(Collectors.toList());
System.out.println(list);
//输出
//[1, 2, 3, 4, 5]
}
//min
public static void main(String[] args) {
Integer[] array = {1, 2, 3, 4, 5};
Optional<Integer> min = Stream.of(array).min(Comparator.comparing(Integer::intValue));
min.ifPresent(System.out::println);
//输出
//1
}
//max
public static void main(String[] args) {
Integer[] array = {1, 2, 3, 4, 5};
Optional<Integer> max = Stream.of(array).max(Comparator.comparing(Integer::intValue));
max.ifPresent(System.out::println);
//输出
//5
}
//count
public static void main(String[] args) {
String[] str = {"a", "b", "c"};
long count = Stream.of(str).count();
System.out.println(count);
//输出
//3
}
短路操作
一旦满足或不满足条件,就结束计算,不用处理完所有元素
- anyMatch:只要有一个符合条件就返回 true
- allMatch:所有都符合条件返回 true
- noneMatch:所有数据都不符合条件返回true
- findFirst:获取第一个元素
- findAny:获取任一元素,一般用于并行流
//anyMatch
public static void main(String[] args) {
String[] str = {"banana", "apple", "pineapple", "pear", "watermelon"};
boolean b = Stream.of(str).anyMatch(t -> t.length() == 4);
boolean b2 = Stream.of(str).anyMatch(t -> t.length() == 3);
System.out.println(b);
System.out.println(b2);
//输出
//true
//false
}
//allMatch
public static void main(String[] args) {
String[] str = {"banana", "apple", "pineapple", "pear", "watermelon"};
boolean b = Stream.of(str).allMatch(t -> t.length() >= 4);
boolean b2 = Stream.of(str).allMatch(t -> t.length() >= 5);
System.out.println(b);
System.out.println(b2);
//输出
//true
//false
}
//noneMatch
public static void main(String[] args) {
String[] str = {"banana", "apple", "pineapple", "pear", "watermelon"};
boolean b = Stream.of(str).noneMatch(t -> t.length() >= 4);
boolean b2 = Stream.of(str).noneMatch(t -> t.length() <= 3);
System.out.println(b);
System.out.println(b2);
//输出
//false
//true
}
//findFirst
public static void main(String[] args) {
String[] str = {"banana", "apple", "pineapple", "pear", "watermelon"};
Optional<String> first = Stream.of(str).findFirst();
first.ifPresent(System.out::println);
//输出
//banana
}
//findAny
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.parallelStream()
.filter(n -> n % 2 == 0)
.findAny().ifPresent(System.out::println);
//输出
//2 or 4
}