Stream流与Lambda表达式学习

介绍:

Stream是Java8引入的一个强大的数据处理工具,它提供了一种流畅,功能强大且高效的方式来处理集合数据。Stream流可以将复杂的数据处理操作用一种简单,易读的方式表达出来,使代码更加清晰。

Stream的实现:

  1. ReferencePipeline类 :在Java 8中,Stream 操作的主要实现类,它继承AbstractPipeline,也定义了一些静态内部类来完成具体的操作。
    Head :表示流的起始点,即最初的数据源,也实现了一些最基本的操作,比如 forEach、iterator 等。
    StatelessOp :表示无状态的中间操作,比如 map、filter 等。它继承 ReferencePipeline,实现了一系列无状态中间操作的逻辑
    StatefulOp:表示有状态的中间操作,比如 sorted、distinct 等。继承 ReferencePipeline,实现了一系列有状态中间操作的逻辑。
  2. Stream 接口:定义了对一组元素进行连续操作的方法,如 map、filter、reduce 等,这个接口没有提供具体的实现。
  3. AbstractPipeline类:Stream 接口的一个抽象实现,它实现了 BaseStream 接口并实现了BaseStream接口中定义的方法。
  4. BaseStream 接口: Stream接口的父接口,定义了一些基本的操作,比如iterator等。扩展了AutoCloseable接口,支持流的关闭操作。BaseStream接口也是一个泛型接口,它有两个类型参数,第一个表示流中的元素类型,第二个表示流本身的类型(通常是Stream或其子接口的类型)。
  5. PipelineHelper类:用于帮助构建流式操作的流水线。提供了一些方法和工具,用于创建和管理流式操作的各个组成部分,例如流的源头、中间操作和终止操作等。在流式操作的实现中起到了关键的作用,帮助实现流的顺畅执行和数据处理。
  6. 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);
相关推荐
loveLifeLoveCoding5 分钟前
Java List sort() 排序
java·开发语言
草履虫·12 分钟前
【Java集合】LinkedList
java
AngeliaXue14 分钟前
Java集合(List篇)
java·开发语言·list·集合
世俗ˊ15 分钟前
Java中ArrayList和LinkedList的比较
java·开发语言
zhouyiddd19 分钟前
Maven Helper 插件
java·maven·intellij idea
攸攸太上28 分钟前
Docker学习
java·网络·学习·docker·容器
Milo_K35 分钟前
项目文件配置
java·开发语言
程序员大金39 分钟前
基于SpringBoot+Vue+MySQL的养老院管理系统
java·vue.js·spring boot·vscode·后端·mysql·vim
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS网上购物商城(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
nsa652231 小时前
Knife4j 一款基于Swagger的开源文档管理工具
java