200字
stream()
2024-08-29
2025-05-09

filter()

filter 方法接受一个 Predicate 函数式接口,该接口定义了一个测试条件。流中的每个元素都会通过这个测试条件,只有那些满足条件的元素会被保留在过滤后的流中。

语法:

CodeStream<T> filter(Predicate<? super T> predicate);

ps:

 List<Event> evenList = Arrays.asList(
                new Event("C", "迷茫"),
                new Event("L", "迷茫"),
                new Event("Z", "认真"),
                new Event("M", "开心"),
                new Event("W", "悲伤")
        );

        // 输出 Event{name='c', status='迷茫'} Event{name='l', status='迷茫'}
        evenList.stream().filter(e -> e.getStatus().equals("迷茫")).collect(Collectors.toList()).forEach(System.out::println);

特性:

  1. 中间操作: filter 是一个中间操作,这意味着它返回一个新的流,原始流不受影响。多个中间操作可以链式调用。
  2. 惰性求值: filter 操作在流的终端操作(如 collect)执行之前不会实际执行。
  3. 不改变原始数据: filter 不会改变原始的集合数据,它只是创建了一个新的流来包含符合条件的元素。

map()

语法:

Code<R> Stream<R> map(Function<? super T, ? extends R> mapper);

ps:

List<Event> eventList = Arrays.asList(
                        new Event("C", 1),
                        new Event("L", 1),
                        new Event("Z", 3),
                        new Event("W", 2),
                        new Event("M", 3)
                );
        // 输出 C L Z W M
        eventList.stream().map(Event::getName).collect(Collectors.toList()).forEach(System.out::println);

特性

  1. 中间操作: map 是一个中间操作,返回一个新的流,原始流不受影响。多个中间操作可以链式调用。
  2. 惰性求值: map 操作在流的终端操作(如 collect)执行之前不会实际执行。
  3. 转换数据: map 不会修改原始数据结构,它会生成一个新的流,其中包含转换后的元素。

常见用法

  1. 对象转换: 将对象的属性从一个形式转换为另一个形式。例如,将字符串转换为其长度。
  2. 数据格式转换: 将数据转换为另一种格式。例如,将数字格式化为字符串。
  3. 计算: 对流中的元素进行计算,例如计算每个数的平方、增加一定的值等。

sorted(Comparator.comparing())

语法

sorted()

CodeStream<T> sorted(Comparator<? super T> comparator);

语法:

Comparator.comparing()Comparator 接口的一个静态方法,用于创建一个比较器,该比较器根据对象的某个特定属性进行排序。通常用法是先通过 Comparator.comparing() 创建一个比较器,再将其传递给 sorted() 方法。

Codestatic <T, U> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor, Comparator<? super U> keyComparator);
  • keyExtractor 是一个函数,用于提取用于比较的键。
  • keyComparator 是一个比较器,用于比较键。如果不提供 keyComparator,则默认按自然顺序进行排序。

假设我们有一个字符串列表,我们想要按照字符串长度进行排序:

 List<String> words = Arrays.asList("01", "012345", "01234", "0123", "012");
        /**
         * 输出
         * 01
         * 012
         * 0123
         * 01234
         * 012345
         */
words.stream().sorted(Comparator.comparing(String::length)).collect(Collectors.toList()).forEach(System.out::println);

sorted().limit()

limit(long maxSize) 是一个终端操作,用于限制流中返回的元素数量。它会返回一个包含流前 maxSize 个元素的新流。注意,limit() 在实际计算流时会应用截断,所以它对流的性能和结果有实际影响。

比如 按 num 属性从大到小排序后的前三个事件:

 List<Event> list = Arrays.asList(
                new Event("C", "1", 2),
                new Event("L", "1", 3),
                new Event("Z", "2", 1),
                new Event("W", "4", 2),
                new Event("M", "2", 4)
        );

List<Event> a2 = list.stream().sorted((e1,e2) -> Integer.compare(e2.getNum(),e1.getNum())).limit(3).collect(Collectors.toList());
//[Event{name='M', num=4}, Event{name='L', num=3}, Event{name='C', num=2}]

reduce()

reduce() 方法有两种形式:

  1. reduce(BinaryOperator<T> accumulator):接收一个累积函数,对流中的元素进行累积,最终得到一个 Optional<T> 结果。
  2. reduce(T identity, BinaryOperator<T> accumulator):接收一个初始值和一个累积函数,进行累积操作,最终返回一个结果类型为 T 的值。

​ 假设我们有一个整数列表,我们想要计算所有元素的总和:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

int sum = numbers.stream()
                 .reduce(0, (a, b) -> a + b);

System.out.println("Sum: " + sum);  // 输出:Sum: 15

​ 假设我们有一个整数列表,想找到其中的最大值:

 List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        Optional<Integer> max = numbers.stream()
                                        .reduce((a, b) -> a > b ? a : b);

        max.ifPresent(System.out::println);  // 输出: 5

总结

  • reduce() 用于将流中的元素进行累积操作,得到最终结果。
  • 形式 reduce(BinaryOperator<T> accumulator) 返回 Optional<T>,适用于没有初始值的情况。
  • 形式 reduce(T identity, BinaryOperator<T> accumulator) 返回类型 T 的值,适用于有初始值的情况。
  • 常见用例包括计算总和、找最大值、拼接字符串等。

anyMatch()

语法

Codeboolean anyMatch(Predicate<? super T> predicate)

​ 假设我们有一个整数列表,想要检查是否包含任何负数:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 检查是否有负数
        boolean hasNegative = numbers.stream()
                                     .anyMatch(n -> n < 0);// 输出: false

​ 假设我们有一个字符串列表,想要检查是否包含某个特定的字符串:

 List<String> words = Arrays.asList("apple", "banana", "cherry");

        // 检查是否包含"banana"
        boolean containsBanana = words.stream()
                                      .anyMatch(word -> word.equals("banana"));

        System.out.println(containsBanana);  // 输出: true

总结

  • anyMatch() 用于检查流中是否有至少一个元素满足给定的条件。
  • 如果流中存在满足条件的元素,anyMatch() 会返回 true,否则返回 false
  • 这是一个短路操作,一旦找到符合条件的元素,就会停止后续的处理,提高效率。

flatMap()

语法

Code<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

flatMap() 方法用于处理嵌套的流,将多个流合并为一个流。以下是一个示例:

 List<String> sentences = Arrays.asList("这里是", "不爱", "吃鱼");

        // 使用 flatMap 将每个句子拆分成单词,并将所有单词扁平化到一个流中
        List<String> words = sentences.stream()
                                      .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
                                      .collect(Collectors.toList());
// 输出: [这里是, 不爱, 吃鱼]

假设我们有一个用户列表,每个用户有一个订单列表。我们想要提取所有用户的所有订单:

 List<User> users = Arrays.asList(
            new User("高木", Arrays.asList(new Order("苹果"), new Order("香蕉"))),
            new User("西片", Arrays.asList(new Order("西瓜"), new Order("哈密瓜")))
        );

        // 使用 flatMap 提取所有订单
        List<Order> allOrders = users.stream()
                                     .flatMap(u -> u.orders.stream())
                                     .collect(Collectors.toList());

        System.out.println(allOrders);  // 输出: [苹果, 香蕉, 西瓜, 哈密瓜]

总结

  • flatMap() 用于将流中的每个元素映射成一个流,并将所有这些流的元素扁平化成一个单一的流。
  • map() 方法不同,flatMap() 允许每个元素映射成零个或多个元素,从而提供了更强的灵活性。
  • 常用于处理嵌套集合或需要将多个流合并成一个流的场景。

peek()

语法

CodeStream<T> peek(Consumer<? super T> action)

peek() 方法允许在流的每个元素上执行操作,但不会改变流中元素的类型或数量。它通常用于调试或记录流处理过程中的中间状态,或者对流中的元素进行某种副作用操作。

假设有一个字符串列表,我们希望在转换成大写之前输出每个字符串的长度:

List<String> words = Arrays.asList("apple", "banana", "orange");

List<String> capitalizedWords = words.stream()
                                    .peek(s -> System.out.println( s + ": " + s.length()))
                                    .map(String::toUpperCase)
                                    .collect(Collectors.toList()); //流转列表
// capitalizedWords输出为:apple: 5 banana: 6 orange: 6 [APPLE, BANANA, ORANGE]

注意

  • peek() 不会修改流的元素,它只是观察流中的元素状态。
  • 主要用于调试和日志记录,不应在生产代码中滥用它来执行关键逻辑。
  • peek() 的操作是中间操作,它不会触发流的处理,流的处理会在终端操作(如 forEach())时完成。

总结

  • peek() 是一个中间操作,用于在流操作过程中查看每个元素的状态。
  • 适用于调试和记录日志,但不应在流中进行实际的元素修改。
  • 它允许在流的处理中插入调试信息而不改变流的内容。

mapToDouble().sum()

语法

CodeDoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)

语法

Codedouble sum()

假设有多个账户金额,想拿到总的金额:

double totalAmount = orders.stream()
                .mapToDouble(Order::getAmount)
                .sum();

总结

  • mapToDouble() 用于将流中的元素映射为 double 类型的流。
  • sum() 用于计算 DoubleStream 中所有 double 值的总和。
  • 这两个方法的组合非常适用于需要计算数值数据总和的场景。

评论