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 id
s (id
s 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());