Home > OS >  How to filter element from a List using Java 8 streams?
How to filter element from a List using Java 8 streams?

Time:04-16

I have a simple Java 8 example using filter() operation.

When I try to filter, I'm getting an empty list.

class Per {
    String id;

    Per(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
    public static void main(String[] args) {

        List<String> allIds = new ArrayList<>();
        allIds.add("1");
        allIds.add("2");
        allIds.add("3");
        allIds.add("4");

        List<Per> unavailableItems = new ArrayList<>();
        unavailableItems.add(new Per("2"));
        unavailableItems.add(new Per("4"));

        List<Per> ids = unavailableItems.stream().filter(per -> !(allIds.contains(per.getId())))
                .collect(Collectors.toList());
        System.out.println(ids);// why its returning empty value


    }
}

Why am getting an empty list?

CodePudding user response:

Read filter as:

"Should I include (filter) this item, yes or no"

So when your code does the following:

List<Per> ids = unavailableItems.stream().filter(per -> !(allIds.contains(per.getId())))

Then your code checking if it should remove the item Per("2")

And the logic is "if allIds has a "2"? then no, don't filter it, don't include it (that's the ! in your code)

And indeed, you're not including it.

Then repeats with Per("4") and you don't include it either.

The result of not including the two elements is an empty lis.

I hope this helps to clarify it.

Probably you think "filter" means, "filter out" or "exclude" but is the opposite.

It seems to me that you're trying to do the opposite, filter from allIds those elements that are in unavailableItems

In that case:

    List<String> allAvailable = allIds.stream().filter( id -> {
        for (Per p : unavailableItems) {
          if ( p.getId().equals(id)) {
            return false;
          }
        }
        return true;

    }).collect(Collectors.toList());

    System.out.println(allAvailable);

Does exactly what you need and prints:

[1, 3]

CodePudding user response:

you have a not sign in front of your filter condition:

!(allIds.contains(per.getId()))

So instead of filtering for items that are inside the list you are filter for items that are not inside the list. You unavailibleItems list only contains items with a matching item in the allIds list which means this condition is never going to be true. However if you removed the not sign it would work or if you added items into the unavailibleItems list that did not have a matching value in the allId's list it would work.

CodePudding user response:

You are getting an empty list because operation filter() will retain in the stream only elements that match the given predicate.

Returns a stream consisting of the elements of this stream that match the given predicate.

Since all values that have in the source list are present in the allIds list predicate always returns false.

If you want to retain only elements that exist in the allIds list, change your predicate to:

filter(per -> allIds.contains(per.getId()))

To reduce time complexity of contains() checks, you might also make allIds to be HashSet.


If you want to do the opposite check - find a list of available ids (ids from allIds list that are not present in the unavailableItems) you can do it like this:

List<String> availableId = allIds.stream()
    .filter(id -> unavailableItems.stream()
                                  .noneMatch(pers -> pers.getId() == id))
    .collect(Collectors.toList());

And if that is what you're trying to achieve, my suggestion is to improve the code above by generating a Map<String, Per> - person by id based on the availableId list. And check whether the id is not present in the map instead of iterating over the whole availableId list.

Map<String, Per> personById = unavailableItems.stream()
    .collect(Collectors.toMap(Per::getId, Function.identity()));
    
List<String> availableId = allIds.stream()
    .filter(id -> !personById.containsKey(id))
    .collect(Collectors.toList());
  • Related