Java中set集合常用的stream方法,你了解哪几种?

354 阅读13分钟

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:掘金/C站/腾讯云/阿里云/华为云/51CTO(全网同号);欢迎大家常来逛逛,互相学习。

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

  在日常Java开发中,我们都知道SetCollection 接口的一个子接口,它表示一个不允许重复元素的集合。Set接口的常见实现类有 HashSetLinkedHashSetTreeSet。而在Java 8 起,引入了 Stream API,使得对集合的操作更加简洁和高效。对于 Set 集合,可以利用 Stream API 提供的各种方法来进行数据过滤、转换、排序等操作。

  下面是一些常用的 Set 集合与 Stream 方法的示例和用法,看看你们都有用过那几种?

正文

1. 将 Set 转换为 Stream

  首先,您需要将 Set 转换为 Stream,然后才能使用 Stream 提供的各种操作。

/**
 * @Author 喵手
 */
public class SetStreamExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(Arrays.asList("Java", "Python", "JavaScript", "Ruby"));

        // 将 Set 转换为 Stream
        set.stream().forEach(System.out::println);
    }
}

输出结果:

Java
Python
JavaScript
Ruby

实际运行结果展示如下:

image.png

2. filter():过滤元素

  filter() 方法用于过滤集合中的元素,可以根据指定的条件进行筛选。

import java.util.*;

/**
 * @Author 喵手
 * @date: 2025-04-14
 */
public class SetStreamFilterExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(Arrays.asList("Java", "Python", "JavaScript", "Ruby"));

        // 使用 filter 过滤包含 "Java" 的元素
        set.stream()
                .filter(e -> e.contains("Java"))
                .forEach(System.out::println);
    }
}

输出结果:

Java
JavaScript

实际运行结果展示如下:

image.png

3. map():转换元素

  map() 方法用于将集合中的元素转换为其他类型或修改元素的内容。

import java.util.*;

/**
 * @Author 喵手
 * @date: 2025-04-14
 */
public class SetStreamMapExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(Arrays.asList("java", "python", "javascript", "ruby"));

        // 将所有元素转换为大写
        set.stream()
                .map(String::toUpperCase)  // 使用 map 转换为大写
                .forEach(System.out::println);
    }
}

输出结果:

JAVA
PYTHON
JAVASCRIPT
RUBY

实际运行结果展示如下:

image.png

4. distinct():去重

  虽然 Set 本身保证元素唯一,但在某些情况下,您可能在流操作中希望去除重复的元素。distinct() 方法用于返回流中不重复的元素。

import java.util.*;

/**
 * @Author 喵手
 * @date: 2025-04-14
 */
public class SetStreamDistinctExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(Arrays.asList("Java", "Python", "Java", "Ruby", "Python"));

        // 使用 distinct 去除重复元素
        set.stream()
                .distinct()
                .forEach(System.out::println);
    }
}

输出结果:

Java
Python
Ruby

实际运行结果展示如下:

image.png

5. sorted():排序

  sorted() 方法用于对集合中的元素进行排序。默认是按照元素的自然顺序进行排序,也可以使用自定义的比较器来排序。

普通排序

import java.util.*;

/**
 * @Author 喵手
 * @date: 2025-04-14
 */
public class SetStreamSortedExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(Arrays.asList("Java", "Python", "JavaScript", "Ruby"));

        // 使用 sorted() 进行排序(按字母升序)
        set.stream()
                .sorted()  // 默认排序
                .forEach(System.out::println);
    }
}

输出结果:

Java
JavaScript
Python
Ruby

实际运行结果展示如下:

image.png

代码解析:

如上这段示例代码的目的是演示如何通过 Java 的流(Stream)API对集合进行排序,并输出排序后的元素。以下是详细的解析,仅供参考:

  1. 创建集合

    Set<String> set = new HashSet<>(Arrays.asList("Java", "Python", "JavaScript", "Ruby"));
    

    这行代码创建了一个 HashSet 集合,并用 Arrays.asList() 方法初始化了几个字符串元素。这里的元素是 "Java", "Python", "JavaScript", "Ruby"。HashSet 是一个无序集合,元素的顺序是不确定的。

  2. 流的操作

    set.stream()
        .sorted()  // 默认排序
        .forEach(System.out::println);
    
    • set.stream():将集合 set 转换为流。流(Stream)是 Java 8 引入的一种新方式,它可以更简洁、声明性地处理集合数据。

    • sorted():这是一个中间操作,用于对流中的元素进行排序。默认情况下,sorted() 方法会按元素的自然顺序(即字母顺序,对于字符串来说就是字典序)对元素进行升序排序。

    • forEach(System.out::println):这是一个终止操作,用于遍历流中的每个元素,并执行 System.out::println 方法来输出每个元素。

  3. 注意事项

    • HashSet 本身是不保证元素顺序的,但流(Stream)可以对元素进行排序,因此这里使用 stream()sorted() 来实现排序功能。
    • 由于 sorted() 是一个返回新流的操作,因此它不会修改原始的 set,而是返回一个排序后的新流。

总结来说,这段代码演示了如何使用 Java 8 的流式操作对集合中的元素进行排序并输出,使用的是默认的字母升序排序。

自定义排序

  如果你想按照自定义规则进行排序,可以使用 Comparator

import java.util.*;

/**
 * @Author 喵手
 * @date: 2025-04-14
 */
public class SetStreamSortedCustomExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(Arrays.asList("Java", "Python", "JavaScript", "Ruby"));

        // 使用自定义的 Comparator 进行排序(按字符串长度升序)
        set.stream()
                .sorted(Comparator.comparingInt(String::length))
                .forEach(System.out::println);
    }
}

输出结果:

Ruby
Java
Python
JavaScript

实际运行结果展示如下:

image.png

6. collect():收集结果

  collect() 方法用于将流的元素收集到一个容器中,如集合或列表。它是 Stream API 中最常用的方法之一。

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author 喵手
 * @date: 2025-04-14
 */
public class SetStreamCollectExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(Arrays.asList("Java", "Python", "JavaScript", "Ruby"));

        // 将流的结果收集到一个 List 中
        List<String> list = set.stream()
                .collect(Collectors.toList());

        System.out.println(list);
    }
}

输出结果:

[Java, Python, JavaScript, Ruby]

实际运行结果展示如下:

image.png

7. reduce():聚合元素

  reduce() 方法用于对流中的元素进行聚合。你可以使用它来做加法、乘法等聚合操作。

import java.util.*;

/**
 * @Author 喵手
 * @date: 2025-04-14
 */
public class SetStreamReduceExample {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));

        // 使用 reduce() 计算所有元素的总和
        int sum = set.stream()
                .reduce(0, (a, b) -> a + b);

        System.out.println("Sum: " + sum);
    }
}

输出结果:

Sum: 15

实际运行结果展示如下:

image.png

8. anyMatch()allMatch()noneMatch():匹配操作

这几个方法用于检查流中是否有元素满足给定条件。

  • anyMatch():检查是否有任何元素满足条件。
  • allMatch():检查是否所有元素都满足条件。
  • noneMatch():检查是否没有任何元素满足条件。
import java.util.*;

/**
 * @Author 喵手
 * @date: 2025-04-14
 */
public class SetStreamMatchExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(Arrays.asList("Java", "Python", "JavaScript", "Ruby"));

        // 使用 anyMatch 判断是否有元素包含 'Java'
        boolean anyMatch = set.stream()
                .anyMatch(e -> e.contains("Java"));
        System.out.println("Contains 'Java': " + anyMatch);

        // 使用 allMatch 判断是否所有元素包含 'a'
        boolean allMatch = set.stream()
                .allMatch(e -> e.contains("a"));
        System.out.println("All contain 'a': " + allMatch);
    }
}

输出结果:

Contains 'Java': true
All contain 'a': false

实际运行结果展示如下:

image.png

归纳:常用的Stream方法

方法作用示例应用
filter()过滤元素,根据条件筛选过滤掉不符合条件的元素
map()转换元素,进行映射操作将元素转换为另一种类型或值
distinct()去重去掉集合中重复的元素
sorted()排序按照自然顺序或自定义规则排序元素
collect()收集流中的元素到集合将流收集到ListSet等集合中
reduce()聚合操作,通过二元操作合并流中的元素计算集合元素的总和、最大值等
anyMatch()判断是否有元素符合条件判断集合中是否有元素满足某条件
allMatch()判断是否所有元素符合条件判断集合中是否所有元素满足某条件
noneMatch()判断是否没有元素符合条件判断集合中是否没有元素满足某条件
forEach()遍历集合中的元素并执行操作对集合中的每个元素执行操作

  通过利用这些常用的Stream方法,我们可以简洁而高效地处理Java集合数据,完成常见的数据操作,如过滤、转换、排序和聚合等。

  接下来,我们来扒拉下上述每个方法的源码,进行深度理解,学习其底层原理。

源码剖析

1. filter()

  filter() 方法的源码是通过 AbstractStream 类实现的。其核心逻辑是通过 Predicate 过滤流中的元素。

@Override
public Stream<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    return new StreamSpliterators.FilterOp<>(this, predicate);
}
  • filter() 方法会返回一个新的流,该流仅包含满足 predicate 条件的元素。FilterOpStreamSpliterators 的一个内部类,它会在迭代流时应用过滤条件。

2. map()

  map() 方法用于转换流中的元素,同样是在 AbstractStream 中实现。

@Override
public <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
    Objects.requireNonNull(mapper);
    return new StreamSpliterators.MapOp<>(this, mapper);
}
  • map() 方法通过传入的 Function,将流中的每个元素映射为一个新的值。内部通过 MapOp 类完成具体操作,生成一个新的流。

3. distinct()

  distinct() 用于去重,它通过 StreamSpliterators.DistinctOp 来完成去重操作。

@Override
public Stream<T> distinct() {
    return new StreamSpliterators.DistinctOp<>(this);
}
  • 该方法返回一个新的流,去除了流中的重复元素,内部使用 Set 来记录已遇到的元素。DistinctOp 负责实际的去重操作。

4. sorted()

  sorted() 方法用来对流中的元素进行排序。

@Override
public Stream<T> sorted() {
    return new StreamSpliterators.SortOp<>(this, null);
}

@Override
public Stream<T> sorted(Comparator<? super T> comparator) {
    return new StreamSpliterators.SortOp<>(this, comparator);
}
  • sorted() 方法有两个重载,一个使用自然顺序排序,另一个使用自定义 Comparator 排序。具体排序操作由 SortOp 类实现。

5. collect()

  collect() 是流操作中的一个终结操作,它通过 Collector 来收集元素。

@Override
public <R, A> R collect(Collector<? super T, A, R> collector) {
    Objects.requireNonNull(collector);
    return collector.finisher().apply(collector.accumulator().apply(collector.supplier(), stream));
}
  • collect() 方法调用 Collector 的方法来执行元素收集操作。它分为 supplier(提供一个空的容器)、accumulator(收集数据)、combiner(并行时合并数据)以及 finisher(最终转换成结果)。

6. reduce()

  reduce() 方法用于将流中的元素合并成一个单一的结果。

@Override
public Optional<T> reduce(BinaryOperator<T> accumulator) {
    Objects.requireNonNull(accumulator);
    return reduce((a, b) -> accumulator.apply(a, b));
}

@Override
public T reduce(T identity, BinaryOperator<T> accumulator) {
    Objects.requireNonNull(accumulator);
    T result = identity;
    for (T element : this) {
        result = accumulator.apply(result, element);
    }
    return result;
}
  • reduce() 方法依赖于二元操作 BinaryOperator 来将流中的元素逐步合并。第二个重载版本支持一个初始值。

7. anyMatch()

  anyMatch() 判断流中是否有元素满足给定条件。

@Override
public boolean anyMatch(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    for (T t : this) {
        if (predicate.test(t)) {
            return true;
        }
    }
    return false;
}
  • anyMatch() 遍历流中的元素,只要有一个元素满足条件,就返回 true,否则返回 false

8. allMatch()

  allMatch() 判断流中所有元素是否都满足给定条件。

@Override
public boolean allMatch(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    for (T t : this) {
        if (!predicate.test(t)) {
            return false;
        }
    }
    return true;
}
  • allMatch() 方法会检查每一个元素,如果任何一个元素不符合条件,立即返回 false

9. noneMatch()

  noneMatch() 判断流中是否没有元素符合给定条件。

@Override
public boolean noneMatch(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    for (T t : this) {
        if (predicate.test(t)) {
            return false;
        }
    }
    return true;
}
  • noneMatch() 遍历流中的元素,如果有任何元素满足条件,则返回 false,否则返回 true

10. forEach()

  forEach() 遍历流中的元素并对每个元素执行给定的操作。

@Override
public void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}
  • forEach() 方法遍历流中的每个元素,并对每个元素执行 Consumer 给定的操作。

小结

  在本文中,我们深入探讨了Java 8的Stream API如何与Set集合结合使用,极大简化了集合操作的代码量,同时提升了开发效率。通过示例代码,我们展示了Stream API中的常用操作方法及其实际应用,包括:filter()map()distinct()sorted()collect()reduce()anyMatch()allMatch()noneMatch()、以及forEach()等。这些方法使得集合的操作更加简洁、声明性和高效,避免了传统的循环和条件判断,提供了一种更加函数式的编程风格。

关键点回顾

  1. filter(): 用于过滤集合中的元素,基于给定的条件进行筛选。
  2. map(): 用于转换集合中的元素,常用于类型转换或内容修改。
  3. distinct(): 去除重复元素,尽管Set保证了唯一性,但流操作中可能需要显式去重。
  4. sorted(): 排序集合元素,可以使用自然顺序或者自定义的比较器。
  5. collect(): 用于将流收集到集合中,如ListSet等。
  6. reduce(): 进行聚合操作,将集合中的元素通过某种规则合并成一个结果,常用于求和、求最大值等。
  7. 匹配操作: anyMatch()allMatch()noneMatch()用于判断流中的元素是否符合给定条件,提供了不同的匹配策略。

流的底层实现

  在底层实现方面,Stream API的每个操作方法都通过中间操作和终止操作的组合来实现。例如,filter()会返回一个新的流,其中的元素经过Predicate筛选;map()会根据给定的Function对每个元素进行转换,形成新的流;sorted()会根据自然顺序或自定义比较器对流中的元素进行排序。

  这些方法的实现往往涉及到流式迭代器(如StreamSpliterators.FilterOpStreamSpliterators.MapOp等),它们负责在流的遍历过程中应用具体的操作,如过滤、转换、排序等。

Stream API的优势

  1. 简洁性: 使用Stream API可以将原本繁琐的集合操作(如遍历、过滤、转换等)通过链式调用简洁地表达出来,代码更加清晰。
  2. 并行化支持: Stream API支持并行操作,只需要调用parallelStream(),即可通过多核处理器并行处理集合中的元素。
  3. 函数式编程支持: Stream API的操作方法大多数都是基于函数式编程思想设计的,使得代码更加模块化、灵活。

应用场景

  Stream API非常适用于大规模数据处理、集合数据转换和复杂的条件筛选等场景。在现代开发中,尤其是处理API、数据库查询结果、文件流等数据源时,Stream API能够显著提升代码的可读性和执行效率。

总结

  通过熟练掌握Stream API及其各种方法,相信大家可以更高效地处理Java集合中的数据,简化常见的数据操作,并且提升代码的可维护性。结合Lambda表达式,Stream API提供了强大的函数式编程能力,可以让你用更少的代码完成更多的任务。如果你还没有完全掌握Stream API,不妨通过实际的编程练习,深入理解每个方法的使用场景和底层原理,进一步提高你的Java编程水平。

... ...

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

... ...

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。


版权声明:本文由作者原创,转载请注明出处,谢谢支持!