ZetCode

Java Stream 过滤

最后修改于 2025 年 5 月 25 日

在本文中,我们将探讨如何使用过滤操作有效地过滤 Java 流。过滤是函数式编程中的一项基本技术,可帮助优化数据集,提取相关信息并简化 Java 流中的数据处理。

Java Stream 表示来自数据源的一系列元素,支持各种聚合操作,例如过滤、映射、归约和排序。与集合不同,流不会永久存储元素;相反,它们按需处理元素,使其对于大型数据集非常有效。Java 流可以对集合、数组、I/O 资源和其他来源进行操作,从而提供了一种函数式的数据操作方法。

流聚合操作类似于 SQL 查询,允许开发人员以最少的代码执行复杂的数据转换。通过函数式编程原则,流可以实现以下操作:

Java 流的关键优势之一是它们能够链接多个操作,从而生成简洁、可读且高效的代码。与依赖外部迭代的传统集合不同,Java 流利用内部迭代,将执行控制委托给运行时以进行优化处理。

filter 方法

Java Stream 的 filter 方法是一个中间操作,旨在选择满足指定条件的元素。它接受一个谓词函数,该函数评估每个元素并返回一个布尔值,指示该元素是否应包含在最终流中。

在处理大型数据集时,此方法特别有用,它允许开发人员优化和提取相关数据,而无需修改原始集合。

filter 方法可以与其他流操作组合使用,以进行复杂的数据处理,从而使 Java 流成为现代应用程序开发的强大工具。

按字符串长度过滤

以下示例过滤字符串列表。

Main.java
void main() {

    List<String> words = List.of("pen", "custom", "orphanage",
            "forest", "bubble", "butterfly");

    List<String> result = words.stream().filter(word -> word.length() > 5).toList();

    result.forEach(System.out::println);
}

我们有一个单词列表。我们过滤该列表,仅包含长度大于 5 的字符串。

List<String> result = words.stream().filter(word -> word.length() > 5).toList();

使用 stream 方法,我们从字符串列表创建一个 Java Stream。在此流上,我们应用 filter 方法。 filter 方法接受一个匿名函数,该函数对于流中所有长度大于 5 的元素返回布尔值 true。

result.forEach(System.out::println);

我们使用 forEach 方法遍历结果,并将所有元素打印到控制台。

$ java Main.java
custom
orphanage
forest
bubble
butterfly

过滤 null 值

下一个示例过滤掉 null 值。

Main.java
void main() {

    List<String> words = new ArrayList<>();
    words.add("cup");
    words.add(null);
    words.add("forest");
    words.add("sky");
    words.add("book");
    words.add(null);
    words.add("theatre");
    
    List<String> result = words.stream().filter(Objects::nonNull).toList();

    System.out.println(result);
}

我们有一个单词列表。通过 Stream 过滤操作,我们创建一个新列表,其中丢弃了 null 值。

List<String> result = words.stream().filter(Objects::nonNull).toList();

在 lambda 表达式的主体中,我们检查该值是否不是 nulltoList 方法是一个终端操作,它从过滤后的流中创建一个列表。

多个过滤操作

可以在流上应用多个过滤操作。

Main.java
void main() {

    int[] inums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
    
    IntConsumer icons = i -> System.out.print(i + " ");
    
    Arrays.stream(inums).filter(e -> e < 6 || e > 10)
            .filter(e -> e % 2 == 0).forEach(icons);
}

在此示例中,我们在整数流上应用多个过滤操作。

int[] inums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

我们有一个整数值数组。

IntConsumer icons = i -> System.out.print(i + " ");

IntConsumer 是一个函数式接口,它接受一个整数值并对其执行操作。在这种情况下,它将整数值打印到控制台。

Arrays.stream(inums).filter(e -> e < 6 || e > 10)
        .filter(e -> e % 2 == 0).forEach(icons);

使用 Arrays.stream 方法从数组创建一个流。 执行多个过滤操作。

过滤对象

下一个示例显示如何过滤对象。

Main.java
void main() {

    List<User> persons = List.of(
            new User("Jack", "jack234@gmail.com"),
            new User("Peter", "pete2@post.com"),
            new User("Lucy", "lucy17@gmail.com"),
            new User("Robert", "bob56@post.com"),
            new User("Martin", "mato4@imail.com")
    );

    List<User> result = persons.stream()
            .filter(person -> person.email().matches(".*post\\.com"))
            .toList();

    result.forEach(p -> System.out.println(p.name()));
}

record User(String name, String email) {
}

该示例创建一个 User 对象流。 它过滤掉那些与特定正则表达式匹配的对象。

List<User> result = persons.stream()
        .filter(person -> person.email().matches(".*post\\.com"))
        .toList();

在过滤谓词中,我们选择与 .*post\\.com 模式匹配的电子邮件。

按年龄过滤用户

下一个示例按年龄过滤用户。 我们使用 Period 类根据用户的出生日期计算用户的年龄。

Main.java
void main() {

    List<User> users = List.of(
            new User("John", "Doe", "1990-01-01"),
            new User("Jane", "Doe", "1985-05-15"),
            new User("Alice", "Smith", "2000-12-31"),
            new User("Paul", "Anka", "1965-11-04"),
            new User("Bob", "Brown", "1995-07-20"),
            new User("Charlie", "Johnson", "1980-03-10"),
            new User("Diana", "Prince", "1992-11-11")
    );

    users.stream()
            .filter(user -> user.age() > 40)
            .forEach(user -> System.out.printf(
                    "%s %s is %d years old%n",
                    user.firstName(), user.lastName(), user.age()));
}

record User(String firstName, String lastName, String dateOfBirth) {

    int age() {
        return Period.between(
                LocalDate.parse(dateOfBirth), LocalDate.now()).getYears();
    }
}

filter 方法用于选择年龄大于 40 岁的用户。

$ java Main.java
Paul Anka is 59 years old
Charlie Johnson is 45 years old

按键过滤映射

在以下示例中,我们按键过滤映射。 使用 Map.Entry 接口的 getKey 方法检索键。

Main.java
void main() {

    Map<String, String> hmap = new HashMap<>();
    
    hmap.put("de", "Germany");
    hmap.put("hu", "Hungary");
    hmap.put("sk", "Slovakia");
    hmap.put("si", "Slovenia");
    hmap.put("so", "Somalia");
    hmap.put("us", "United States");
    hmap.put("ru", "Russia");
    
    hmap.entrySet().stream().filter(map -> map.getKey().startsWith("s"))
            .forEach(System.out::println);
}

该示例过滤以字母 s 开头的域名。 我们使用 getKey 方法检索映射条目的键,并使用 startsWith 方法检查它是否以字母 s 开头。

按值过滤映射

在以下示例中,我们按值过滤映射。 使用 Map.Entry 接口的 getValue 方法检索值。

Main.java
void main() {

    Map<String, String> countries = new HashMap<>();

    countries.put("de", "Germany");
    countries.put("hu", "Hungary");
    countries.put("sk", "Slovakia");
    countries.put("si", "Slovenia");
    countries.put("so", "Somalia");
    countries.put("us", "United States");
    countries.put("ru", "Russia");

    countries.entrySet().stream().filter(country -> country.getValue().equals("Slovakia")
                    || country.getValue().equals("Slovenia"))
            .forEach(System.out::println);
}

在此示例中,我们从映射中过滤掉两个国家/地区。 我们使用 getValue 方法检索映射条目的值,并使用 equals 方法检查它是否等于 "Slovakia" 或 "Slovenia" 。

来源

Java Stream 文档

在本文中,我们已经研究了 Java Stream 过滤操作。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。到目前为止,我撰写了 1,400 多篇文章和 8 本电子书。 我在编程教学方面拥有超过十年的经验。

列出所有Java教程