简体   繁体   中英

Java Stream filtering on same fields but different criteria

I have a Person class:

@Data
public class Person {
   private Integer id;
   private String status;
}

And I have a List of Person called personList:

[{ 
    "id": null,
    "status": "inactive"
 },
 { 
    "id": 2,
    "status": "inactive"
 },
 { 
    "id": null,
    "status": "active"
 }]

Now I need to find all people whose status is "inactive" regardless if the person has an Id or not. If a person doesn't have an Id, but the status is "active", include that person as well. I am using Java stream to do the filtering:

 List<Person> inactivePersonList = 
     personList.stream()
               .filter(person -> person.getStatus().equals("inactive"))
               .filter(person -> person.getId() == null && person.getStatus().equals("active"))
               .collect(Collectors.toList());

The result after streaming is that nobody got picked up. The first person didn't get picked up because it didn't pass the second filter, the last person didn't get picked up because it didn't pass the first filter.

I think I can fix it by combining the two filters into one using OR , but I am trying to make each filter simple, so before I make the change, I would like to ask if there is a better way to do it. Thanks.

Filters are additive. They're effectively && -ed together. When you want || a long filter is unavoidable.

List<Person> inactivePersonList = 
    personList.stream()
              .filter(p -> p.getStatus().equals("inactive") ||
                           p.getId() == null && p.getStatus().equals("active"))
              .collect(Collectors.toList());

If you had separate Predicate objects you could .or() them together.

Predicate<Person> inactive = p -> p.getStatus().equals("inactive");
Predicate<Person> activeUnknown = p -> p.getId() == null && p.getStatus().equals("active");

List<Person> inactivePersonList = 
    personList.stream()
              .filter(inactive.or(activeUnknown))
              .collect(Collectors.toList());

Or even:

Predicate<Person> inactive = p -> p.getStatus().equals("inactive");
Predicate<Person> unknown = p -> p.getId() == null;
Predicate<Person> active = p -> p.getStatus().equals("active");

List<Person> inactivePersonList = 
    personList.stream()
              .filter(inactive.or(unknown.and(active)))
              .collect(Collectors.toList());

Use one filter, but add a method to the user, eg isActive() :

@Data
public class Person {
   private Integer id;
   private String status;
   public boolean isActive() {
       return status.equals("active") || id != null;
   }
}

Preferably you should change the way active users are distinguished. If the user is "active" but not active at the same time, then something is going wrong with your class design.

And you might want to use a enum Status { INACTIVE, ACTIVE } instead of the string.

Or use an additional method isUnknown so that your filter is more clear. It doesn't seem to be a good idea to iterate over all persons multiple times.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM