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);
相关推荐
弗拉唐1 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
Red Red1 小时前
网安基础知识|IDS入侵检测系统|IPS入侵防御系统|堡垒机|VPN|EDR|CC防御|云安全-VDC/VPC|安全服务
网络·笔记·学习·安全·web安全
oi771 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3432 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀2 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20202 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深2 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
shuangrenlong2 小时前
slice介绍slice查看器
java·ubuntu
牧竹子2 小时前
对原jar包解压后修改原class文件后重新打包为jar
java·jar