对 Java 中的地图应用多个过滤器

Apply multiple filter to a map in Java

基于以下问题:

public void filterStudents(Map<Integer, Student> studentsMap){
    Map<Integer, Student> filteredStudentsMap = 
        studentsMap.entrySet()
                   .stream()
                   .filter(s -> s.getValue().getAddress().equalsIgnoreCase("delhi"))
                   .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

此过滤器在 dehli 中离开的学生。我如何过滤 dehliamsterdamnew york 中离开的学生?

有没有比过滤三倍原始地图并将三个输出合并在一起更好的方法?

在多个条件下使用一个过滤器:

public void filterStudents(Map<Integer, Student> studentsMap){
    Map<Integer, Student> filteredStudentsMap = 
        studentsMap.entrySet()
                   .stream()
                   .filter(s -> s.getValue().getAddress().equalsIgnoreCase("delhi") ||
                                s.getValue().getAddress().equalsIgnoreCase("amsterdam") ||
                                s.getValue().getAddress().equalsIgnoreCase("new york"))
                   .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

或者您可以使用 Set 进行简化:

public void filterStudents(Map<Integer, Student> studentsMap){
    Map<Integer, Student> filteredStudentsMap = 
        studentsMap.entrySet()
                   .stream()
                   .filter(s -> Set.of("delhi","amsterdam","new york").contains(s.getValue().getAddress().toLowerCase()))
                   .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

Predicate#or(Predicate)逻辑组合两个Predicate

Predicate<Student> livesInDelhi = student -> "delhi".equalsIgnoreCase(student.getAddress());
Predicate<Student> livesInAmsterdam = student -> "amsterdam".equalsIgnoreCase(student.getAddress());
Predicate<Student> livesInNewYork = student -> "new york".equalsIgnoreCase(student.getAddress());

Predicate<Student> livesInAnyOfTheseThreeCities = livesInDelhi.or(livesInAmsterdam).or(livesInNewYork);

一个 filter 调用看起来像

.filter(e -> livesInAnyOfTheseThreeCities.test(e.getValue()))

How could I adapt the fourth lines where you're chaining filtering parameters?

假设我们有一系列城市

final String[] cities = {"delhi", "amsterdam", "new york"};

对于每个 Student,我们可以写一个 Predicate<Student> 并将它们减少 Predicate::or

Predicate<Student> livesInAnyOfGivenCities = 
  Arrays.stream(cities)
    .map(city -> (Predicate<Student>) student -> city.equalsIgnoreCase(student.getAddress()))
    .reduce(Predicate::or)
    .orElseGet(() -> student -> false);

student -> false 在没有给出城市时使用。

List<String> toFilter = Arrays.asList("delhi", "amsterdam", "new york");
Map<Integer, Student> filteredStudentsMap =
        studentsMap.entrySet()
                .stream()
                .filter(s -> toFilter.stream().anyMatch(f -> s.getValue().getAddress().equalsIgnoreCase(f)))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

当然,您应该在这里使用普通的旧式面向对象编程,并创建一个名称有意义的单独方法:

public void filterStudents(Map<Integer, Student> studentsMap){
Map<Integer, Student> filteredStudentsMap = 
    studentsMap.entrySet()
               .stream()
               .filter(s -> s.getValue().liveIn("delhi", "amsterdam", "new york"))
               .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

当然这需要在Studentclass中创建相应的方法,但为什么我们还需要对象和OOP?)

public class Student {

   // other methods of Student

   public boolean liveIn(String... cities) {
     return Arrays.stream(cities).anyMatch(this.city::equals);
   }
}

A​​rray 只是一个例子——您可以使用 set、list 或任何您想要的。 这里的重点是创建一个可以在流 api 中使用的有意义的方法。

你也可以试试这个,如果你不确定,在不久的将来有多少城市可以进入过滤条件..

public void filterStudents(Map<Integer, Student> studentsMap){
final List<String> includedCities = List.of("DELHI", "NEW YORK", "AMSTERDEM", "SOME MORE");

Map<Integer, Student> filteredStudentsMap = 
    studentsMap.entrySet()
               .stream()
               .filter(s -> includedCities.contains(s.toUpperCase()))
               .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

更新(@JoachimSauer 发表评论后):

这个应该好很多..

public void filterStudents(Map<Integer, Student> studentsMap){
final List<String> includedCities = List.of("DELHI", "NEW YORK", "AMSTERDEM", "SOME MORE");

Map<Integer, Student> filteredStudentsMap = 
    studentsMap.entrySet()
               .stream()
               .filter(s -> exclusiveCities.stream().anyMatch(s::equalsIgnoreCase))
               .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

更新(@Holger 发表评论后):

更好..

public void filterStudents(Map<Integer, Student> studentsMap){
final Set<String> includedCities = new TreeSet<>(CASE_INSENSITIVE_ORDER);
Collections.addAll( includedCities , "DELHI", "NEW YORK", "AMSTERDEM", "SOME MORE");

Map<Integer, Student> filteredStudentsMap = 
    studentsMap.entrySet()
               .stream()
               .filter(includedCities::contains))
               .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}