介绍:
Stream是Java8引入的一个强大的数据处理工具,它提供了一种流畅,功能强大且高效的方式来处理集合数据。Stream流可以将复杂的数据处理操作用一种简单,易读的方式表达出来,使代码更加清晰。
Stream的实现:
- ReferencePipeline类 :在Java 8中,Stream 操作的主要实现类,它继承AbstractPipeline,也定义了一些静态内部类来完成具体的操作。
○Head :表示流的起始点,即最初的数据源,也实现了一些最基本的操作,比如 forEach、iterator 等。
○StatelessOp :表示无状态的中间操作,比如 map、filter 等。它继承 ReferencePipeline,实现了一系列无状态中间操作的逻辑
○StatefulOp:表示有状态的中间操作,比如 sorted、distinct 等。继承 ReferencePipeline,实现了一系列有状态中间操作的逻辑。 - Stream 接口:定义了对一组元素进行连续操作的方法,如 map、filter、reduce 等,这个接口没有提供具体的实现。
- AbstractPipeline类:Stream 接口的一个抽象实现,它实现了 BaseStream 接口并实现了BaseStream接口中定义的方法。
- BaseStream 接口: Stream接口的父接口,定义了一些基本的操作,比如iterator等。扩展了AutoCloseable接口,支持流的关闭操作。BaseStream接口也是一个泛型接口,它有两个类型参数,第一个表示流中的元素类型,第二个表示流本身的类型(通常是Stream或其子接口的类型)。
- PipelineHelper类:用于帮助构建流式操作的流水线。提供了一些方法和工具,用于创建和管理流式操作的各个组成部分,例如流的源头、中间操作和终止操作等。在流式操作的实现中起到了关键的作用,帮助实现流的顺畅执行和数据处理。
- AutoCloseable接口 :Java中的一个标准接口,用于表示实现了自动关闭的资源。它定义了一个close()方法,用于关闭资源。实现了AutoCloseable接口的类可以在try-with-resources语句中使用,以确保资源在使用完毕后被正确关闭。
Stream流的创建:
通过数据源(集合,数组等)生成流
1.集合:可以使用集合的 stream() 方法来创建 Stream 流。
bash
List<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
Map<String, Object> map = new HashMap<>();
map.put("aaa", 111);
map.put("bbb", 222);
Stream<String> stream1 = map.keySet().stream();
stream1.forEach(System.out::println);
Stream<Map.Entry<String, Object>> stream2 = map.entrySet().stream();
stream2.forEach(System.out::println);
2.数组:使用 Arrays.stream() 方法可以将数组转换为 Stream 流。
bash
String[] arr2 = {"a","b","c"};
Stream<String> stream3 = Arrays.stream(arr2);
stream3.forEach(System.out::println);
3.Stream.of():该方法可以直接将一组元素转换为 Stream 流。
bash
Stream<String> stream4 = Stream.of("a", "b", "c", "d", "e");
stream4.forEach(System.out::println);
4.Stream.generate() 和 Stream.iterate():可以创建无限流,用于生成一系列元素。
bash
Stream.generate(Math::random).limit(5).forEach(System.out::println);
Stream.iterate(1, n -> n + 1).limit(5).forEach(System.out::println);
5.文件生成流
bash
Stream<String> stream6 = null;
try {
stream6 = Files.lines(Paths.get("D://study/aa.txt"));
} catch (IOException e) {
throw new RuntimeException(e);
}
assert stream6 != null;
stream6.forEach(System.out::println);
中间操作:
Stream 流支持多种中间操作,这些操作可以按照链式结构组合使用,以实现复杂的数据处理需求:
无状态与有状态区别:
无状态的操作不受前面元素的影响,而有状态的操作必须等所有元素处理完后才知道最终的结果,比如排序sorted()操作,它在没有处理完所有元素时,是不能知道最后的排序结果的。
1.filter:过滤掉不符合条件的数据,保留符合条件的数据。
bash
List<String> list = new ArrayList<>();
Collections.addAll(list, "小米", "小杜", "杜姚");
Stream<String> stream = list.stream().filter(e -> e.startsWith("小"));
stream.forEach(System.out::println);
2.map对流中的每个元素进行映射操作。
bash
List<Map<String, Object>> list1 = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("id", 1);
map1.put("type", "苹果");
Map<String, Object> map2 = new HashMap<>();
map2.put("id", 2);
map2.put("type", "雪梨");
list1.add(map1);
list1.add(map2);
list1.stream().map(e -> e.get("type")).forEach(System.out::println);
3.flatMap将一个对象转换成流。
bash
list1.stream()
.flatMap(e -> Arrays.stream(new String[]{(String) e.get("type")}))
.forEach(System.out::println);
4. distinct返回不同元素,相同的只返回一个。
bash
list1.add(map2);
List<Map<String, Object>> collect = list1.stream().distinct().collect(Collectors.toList());
collect.forEach(System.out::println);
5. sorted排序
bash
list1.stream()
.sorted(Comparator.comparing(e -> (Integer) e.get("id"), Comparator.reverseOrder()))
.forEach(System.out::println);
6.limit只取前面几条数据
bash
list1.stream().limit(1).collect(Collectors.toList()).forEach(System.out::println);
7.concat合并两个流为一个流
bash
Stream<String> stream1 = list.stream().filter(e -> Objects.equals(e, "小杜"));
Stream<String> limit = list.stream().limit(1);
Stream.concat(stream1, limit).collect(Collectors.toList()).forEach(System.out::println);
8.skip跳过前面的几条数据
bash
list1.stream().skip(2).collect(Collectors.toList()).forEach(System.out::println);
终端操作:
Stream 流的终端操作用于触发流的处理并生成最终结果,一个流只能有一个终结操作,也必须是最后一个操作:
非短路操作与短路操作区别:
短路操作在满足短路条件时,不需要处理完所有元素就能返回结果;而非短路操作是要处理完所有元素才返回结果,如findFirst找到第一个满足条件的元素就直接返回结果,不用再找后面的数据。
1.forEach遍历流中每个元素
bash
list1.stream().skip(2).collect(Collectors.toList()).forEach(System.out::println);
2. collect将流转化为其他形式
bash
Set<Object> type = list1.stream().map(e -> e.get("type")).collect(Collectors.toSet());
type.forEach(System.out::println);
3.count计算流中元素个数
bash
long count = list1.stream().count();
System.out.printf(String.valueOf(count));
4.reduce通过某个函数将流中的元素归约为一个值
bash
Optional<Integer> sum = list1.stream()
.map(e -> (Integer) e.get("id"))
.reduce(Integer::sum);
sum.ifPresent(s -> System.out.println("Sum: " + s));
5.findFirst查找流中第一条数据
bash
Optional<Map<String, Object>> first = list1.stream().findFirst();
first.ifPresent(s -> System.out.println("first: " + first));
6. findAny查找流中任意一条数据
bash
Optional<Map<String, Object>> findAny = list1.stream().findAny();
findAny.ifPresent(s -> System.out.println("findAny: " + findAny));
7. anyMatch是否集合中有数据都符合条件,只要找到有一个符合条件就返回true,全部都不符合返回false
bash
//list1中是否有id小于1,只要找到有一个小于1就返回true,全部都不小于1返回false
boolean type1 = list1.stream().anyMatch(e -> (Integer) e.get("id") < 1);
System.out.printf("anyMatch:" + type1);
8. allMatch是否集合中全部数据都符合条件,全部符合返回true,有一个不符合都返回false
bash
// 是否list1中全部id都小于1,全部小于返回true,有一个不小于都返回false
boolean type2 = list1.stream().allMatch(e -> (Integer) e.get("id") < 1);
System.out.printf("allMatch:" + type2);
9. noneMatch是否集合中所有元素都不符合条件,只有都不符合条件才返回true,有一个符合条件就返回false
bash
// 是否list1中是否所有role都不小于1,全部不小于返回true,有一个小于返回false
boolean type3 = list1.stream().noneMatch(e -> (Integer) e.get("id") < 1);
System.out.printf("noneMatch:" + type3);
中间操作和终止区别:
中间操作是流水线中的数据进行加工的, 它是一个懒操作,并不会马上执行,需要等待有终止操作的时候才会执行。
终止操作是Stream的启动操作, 当有终止操作的时候, Stream才会真正的开始执行。
bash
List<Integer> list = Arrays.asList(4, 7, 9, 11, 12);
list.stream().peek(e -> System.out.println("stream: " + e));
list.stream().peek(e -> System.out.println("stream: " + e)).collect(Collectors.toList());
并行流与串行流:
Stream 流还支持并行处理,可以提高大数据量处理时的性能。通过使用 parallelStream() 方法来将流转换为并行流,使得多个处理任务可以同时执行。(但是并行流不一定比串行流Stream()快,例如并行流线程管理和数据切分的开销)
Stream流的特性:
惰性求值:Stream 操作不会立即执行,只有在终止操作被调用时才会触发流的处理,这样可以提高性能。终止操作是Stream的启动操作, 当有终止操作的时候, Stream才会真正的开始执行。
Stream与Lambda 表达式:
Stream 用于对集合进行处理和操作,Lambda 表达式用于定义函数或行为,它们结合起来使用更加简洁易懂。
Lambda表达式:
Lambda表达式是Java中支持函数式编程的重要特性。任何一个接口,有且仅有一个抽象方法,那么就是函数式接口,这样才可以使用Lambda表达式来实现。
1.匿名内部类中重写compare ()方法:
方法形式参数为String s1, String s2;方法返回值类型为int类型;方法体中的内容,是我们具体要做的事情
bash
List<String> list = Arrays.asList("Apple", "Orange", "Banana", "Lemon");
list.stream()
.sorted(new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
})
.forEach(System.out::println);
2.Lambda表达式写法:
(形式参数)->{代码块}。()里面为s1,s2,表示方法形式参数;->用箭头指向后面要做的事情;{}包含一段代码可以看成是方法体中的内容,即我们具体要做的事情
bash
list.stream()
.sorted((s1, s2) -> s1.compareTo(s2))
.forEach(System.out::println);
拓展:
只定义了单方法的接口称为FunctionalInterface,用注解@FunctionalInterface标记。例如Comparator,Runnable,Callable接口。在接收FunctionalInterface作为参数的时候,可以把实例化的匿名类改写为Lambda表达。
方法引用:
还可以Stream,Lambda表达式与方法引用结合使用。方法引用是指如果某个方法的签名和接口的抽象方法恰好一致,就可以直接传入方法引用。方法签名只看参数类型和返回类型,不看方法名称,也不看类的继承关系。
3.静态方法引用:
LambdaStream::compareByLength将compareByLength 方法作为静态方法引用传递给 sorted 方法
bash
public class LambdaStream {
public static void lambdaStream() {
List<String> list = Arrays.asList("Apple", "Orange", "Banana", "Lemon");
list.stream()
.sorted(LambdaStream::compareByLength)
.forEach(System.out::println);
}
public static int compareByLength(String a, String b) {
return a.length() - b.length();
}
}
4.实例方法引用:
LambdaStream::convertToUpper 将 convertToUpper 方法作为实例方法引用传递给 map 方法
bash
public class LambdaStream {
public static void lambdaStream() {
List<String> list = Arrays.asList("Apple", "Orange", "Banana", "Lemon");
list.stream()
.map(LambdaStream::convertToUpper)
.forEach(System.out::println);
}
public static String convertToUpper(String str) {
return str.toUpperCase();
}
}
5.构造方法引用:
String::new 将 String类的构造方法作为构造方法引用传递给 map方法
bash
list.stream()
.map(String::new)
.forEach(System.out::println);