ZetCode

Java 过滤 Map

上次修改时间:2025 年 6 月 26 日

在本教程中,我们将演示如何使用 Stream API 在 Java 中过滤 Map。过滤 Map 是处理集合时的常见任务,尤其是在需要提取满足特定条件的条目时。

HashMap 是 Java 中广泛使用的数据结构,用于存储键值对。 HashMap 中的每个键都是唯一的,并映射到单个值。 这使其成为快速查找和高效数据检索的理想选择。

要过滤 HashMap,我们可以利用 Java Stream API 提供的 filter 方法。 此方法接受一个谓词——一个函数式接口,它评估每个条目并根据条件返回 truefalse。 结果是一个新的流,仅包含满足谓词的条目。

例如,您可以过滤 Map 以仅包含值大于某个阈值的条目,或者键与特定模式匹配的条目。 过滤后,可以使用 Collectors.toMap 将流收集回 Map 中。

按值过滤 Map

在第一个例子中,我们过滤 Map 的值。

Main.java
void main() {

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

    capitals.put("svk", "Bratislava");
    capitals.put("ger", "Berlin");
    capitals.put("hun", "Budapest");
    capitals.put("czk", "Prague");
    capitals.put("pol", "Warsaw");
    capitals.put("ita", "Rome");

    Map<String, String> filteredCapitals = capitals.entrySet().stream()
        .filter(e -> e.getValue().startsWith("B"))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    filteredCapitals.entrySet().forEach(System.out::println);
}

我们有一个首都 Map。 我们使用 entrySet 获取 Map 的条目。 然后我们使用 stream 将集合转换为流。

Map<String, String> filteredCapitals = capitals.entrySet().stream()
    .filter(e -> e.getValue().startsWith("B"))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

我们过滤 Map 以仅包含值以 B 开头的键值对。我们使用 getValue 从当前条目获取值。

$ java Main.java
hun=Budapest
ger=Berlin
svk=Bratislava

按键过滤 Map

在下一个例子中,我们按键过滤 Map。

Main.java
void main() {

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

    capitals.put("svk", "Bratislava");
    capitals.put("ger", "Berlin");
    capitals.put("hun", "Budapest");
    capitals.put("czk", "Prague");
    capitals.put("pol", "Warsaw");
    capitals.put("ita", "Rome");

    Map<String, String> filteredCapitals = capitals.entrySet().stream()
        .filter(e -> e.getKey().endsWith("k"))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    filteredCapitals.entrySet().forEach(System.out::println);
}

该示例过滤所有键以字母 k 结尾的条目。

Map<String, String> filteredCapitals = capitals.entrySet().stream()
    .filter(e -> e.getKey().endsWith("k"))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

我们使用 getKey 获取条目的当前键。

$ java Main.java
czk=Prague
svk=Bratislava

按键和值过滤 Map

在下一个例子中,我们按键和值过滤 Map。

Main.java
void main() {

    Map<Integer, String> users = new HashMap<>();

    users.put(1, "John Doe");
    users.put(2, "Roger Roe");
    users.put(3, "Jane Doe");
    users.put(4, "Jack Drake");
    users.put(5, "Peter Morgan");
    users.put(6, "Robert Melnik");

    Map<Integer, String> filtered = users.entrySet().stream()
            .filter(e -> e.getKey() % 2 == 0)
            .filter(e -> e.getValue().split(" ")[1].startsWith("D"))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    filtered.entrySet().forEach(System.out::println);
}

我们有一个用户 Map。 我们过滤掉所有键为偶数且姓氏以字母 D 开头的用户。

$ java Main.java
4=Jack Drake

按年龄过滤

我们有一个用户 Map。 我们按用户的年龄过滤 Map。

Main.java
void main() {

    var dft = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    Map<Integer, User> users = new HashMap<>();
    users.put(1, new User("John Doe", "gardener", LocalDate.parse("1973-09-07", dft)));
    users.put(2, new User("Roger Roe", "driver", LocalDate.parse("1963-03-30", dft)));
    users.put(3, new User("Kim Smith", "teacher", LocalDate.parse("1980-05-12", dft)));
    users.put(4, new User("Joe Nigel", "artist", LocalDate.parse("1983-03-30", dft)));
    users.put(5, new User("Liam Strong", "teacher", LocalDate.parse("2009-03-06", dft)));
    users.put(6, new User("Robert Young", "gardener", LocalDate.parse("1978-11-16", dft)));
    users.put(7, new User("Liam Strong", "teacher", LocalDate.parse("1986-10-23", dft)));

    Map<Integer, User> olderThanForty = users.entrySet().stream().filter(e -> {
        LocalDate dob = e.getValue().dob();
        int age = Period.between(dob, LocalDate.now()).getYears();
        return age > 40;
    }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    olderThanForty.entrySet().forEach(System.out::println);
}

record User(String name, String occupation, LocalDate dob) {
}

我们过滤掉所有大于 40 岁的用户。

Map<Integer, User> olderThanForty = users.entrySet().stream().filter(e -> {
    LocalDate dob = e.getValue().dob();
    int age = Period.between(dob, LocalDate.now()).getYears();
    return age > 40;
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

我们使用 e.getValue().dob() 从用户对象中检索出生日期。 然后我们使用 Period.between(dob, LocalDate.now()).getYears() 计算用户的年龄。 过滤条件是 age > 40;

$ java Main.java
1=User[name=John Doe, occupation=gardener, dob=1973-09-07]
2=User[name=Roger Roe, occupation=driver, dob=1963-03-30]
3=User[name=Kim Smith, occupation=teacher, dob=1980-05-12]
6=User[name=Robert Young, occupation=gardener, dob=1978-11-16]

使用逻辑运算符按多个条件过滤

在此示例中,我们将演示如何使用逻辑运算符(如 AND (&&) 和 OR (||))组合的多个条件来过滤 Map。

Main.java
void main() {

    Map<String, Product> products = new HashMap<>();
    products.put("P001", new Product("Laptop", 899.99, "Electronics", 15));
    products.put("P002", new Product("Coffee Mug", 12.99, "Kitchen", 50));
    products.put("P003", new Product("Smartphone", 599.99, "Electronics", 8));
    products.put("P004", new Product("Book", 19.99, "Education", 25));
    products.put("P005", new Product("Headphones", 149.99, "Electronics", 30));
    products.put("P006", new Product("Desk Chair", 249.99, "Furniture", 5));

    // Filter using AND: Electronics products with price less than 700
    Map<String, Product> electronicsCheap = products.entrySet().stream()
        .filter(e -> e.getValue().category().equals("Electronics") && 
                     e.getValue().price() < 700)
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    System.out.println("Electronics products under $700:");
    electronicsCheap.entrySet().forEach(System.out::println);

    // Filter using OR: Products that are either expensive (>200) or low stock (<10)
    Map<String, Product> expensiveOrLowStock = products.entrySet().stream()
        .filter(e -> e.getValue().price() > 200 || e.getValue().stock() < 10)
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    System.out.println("\nProducts that are expensive (>$200) or low stock (<10):");
    expensiveOrLowStock.entrySet().forEach(System.out::println);

    // Complex condition: (Electronics AND expensive) OR (low stock)
    Map<String, Product> complexFilter = products.entrySet().stream()
        .filter(e -> (e.getValue().category().equals("Electronics") && 
                      e.getValue().price() > 500) || 
                     e.getValue().stock() < 10)
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    System.out.println("\nExpensive Electronics OR low stock products:");
    complexFilter.entrySet().forEach(System.out::println);
}

record Product(String name, double price, String category, int stock) {
}

此示例显示了使用逻辑运算符的三种不同的过滤场景

过滤是使用 Java Stream API 的 filter 方法完成的,该方法允许我们为 Map 中的每个条目指定条件。 使用 collect(Collectors.toMap(...)) 将结果收集到新的 Map 中。

$ java Main.java
Electronics products under $700:
P005=Product[name=Headphones, price=149.99, category=Electronics, stock=30]
P003=Product[name=Smartphone, price=599.99, category=Electronics, stock=8]

Products that are expensive (>$200) or low stock (<10):
P006=Product[name=Desk Chair, price=249.99, category=Furniture, stock=5]
P001=Product[name=Laptop, price=899.99, category=Electronics, stock=15]
P003=Product[name=Smartphone, price=599.99, category=Electronics, stock=8]

Expensive Electronics OR low stock products:
P006=Product[name=Desk Chair, price=249.99, category=Furniture, stock=5]
P001=Product[name=Laptop, price=899.99, category=Electronics, stock=15]
P003=Product[name=Smartphone, price=599.99, category=Electronics, stock=8]

来源

Java HashMap - 语言参考

在本文中,我们展示了如何在 Java 中过滤 Map。

作者

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

列出所有Java教程