-3

I'am trying to filter java collection like this:

filtered = products.stream()
                .filter((product) ->size!=null&& 
                 product.getSize().equalsIgnoreCase(size))
                .filter((product) ->firmness!=null  && 
                 product.getFirmness().equalsIgnoreCase(firmness))
                .collect(Collectors.toList());

In this example i have two variables to filter - size & firmness. This variables are optional and may be nulls. So if i send two not null parameters the code is working and filter fine, but when i send only one parameter and set other parameter to null - it's not working. I need my collection to get filtered by not null values. So if one parameter is null, other is not, the collection must to get filtered by not null value. How i can did this ?

iliya.rudberg
  • 739
  • 12
  • 23

4 Answers4

3

&& requires it to be non-null. Invert the conditions with ||, e.g.,

size == null || product.getSize().equalsIgnoreCase(size)

If you want to avoid the null check for every element, you can put the entire lambda in a ternary expression:

.filter(size == null ? p -> true : p -> p.getSize().equalsIgnoreCase(size))
shmosel
  • 49,289
  • 6
  • 73
  • 138
1

Something like this, I have not compiled it though.
You could compute your Predicate before even starting the pipeline processing.

static  Predicate<Product> predicate(String size, String firmness) {
     if(size != null && firmness != null){
         return p -> p.getSize().equalsIgnoreCase(size) 
                && p.getFirmness().equalsIgnoreCase(firmness);
     } else if(size != null){
         return p -> p.getSize().equalsIgnoreCase(size); 
     } else if(firmness != null){
         return p -> p.getFirmness().equalsIgnoreCase(firmness);
     } else {
         return p -> false;
     }
 }
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • You will get wrong filtering if `firmness` or `size` are `null` but that's because some mixup what this answer really needs is a predicate for when they are both `null`. – Oleg Oct 03 '17 at 20:49
  • @Oleg now that I've edited I don't really like the extra verbosity here :) still thank you for correcting me – Eugene Oct 03 '17 at 20:58
  • I actually like it better with one filter, I guess it's a matter of preference. Also if I understand the question correctly the `else` should be `true`. – Oleg Oct 03 '17 at 21:00
0

Also look at CollectionUtil.retainIf(collection, predicate) from BlobCity java-commons https://blog.blobcity.com/2018/07/15/retainif-and-removeif-for-java-collections-blobcity/

CollectionUtil.retainIf(products, product -> product.getSize().equalsIgnoreCase(size));
CollectionUtil.retainIf(products, product -> product.getFirmness().equalsIgnoreCase(firmness));

Each call reduces the original list to keep only those values that satisfy the predicate condition. null checks can be added within the predicate if desired.

-1

Update. Thanks to shmosel's answer there is a better way to use ternary operation in this lambda example:

filtered = products.stream()
.filter( size == null ? p -> true : p -> p.getSize().equalsIgnoreCase(size) )
.filter( firmness == null ? p -> true : p -> product.getFirmness().equalsIgnoreCase(firmness) )
.collect( Collectors.toList() );

As Holger mentioned moving null check out of lambda expression will avoid repeated checks for null for every product and avoid unnecessary capturing or size and firmness when they are null which help performance according to this.

And here is the original approach:

filtered = products.stream()
.filter( (product) -> size != null ? product.getSize().equalsIgnoreCase(size) : true )
.filter( (product) -> firmness != null ? product.getFirmness().equalsIgnoreCase(firmness) : true )
.collect( Collectors.toList() );
tsolakp
  • 5,858
  • 1
  • 22
  • 28
  • Awesome. I'm trying to use ternary operator before. But my mistake was that the second case was false,not 'true' like in your example. i'm changed it to true and it is working now.thanks ! – iliya.rudberg Oct 03 '17 at 20:26
  • Ternary expressions should never return boolean literals. – shmosel Oct 03 '17 at 20:27
  • @shmosel Can you base that on something? (Not arguing, curious) Looks fine to me in this case. – Oleg Oct 03 '17 at 20:32
  • 1
    @Oleg You can always use logical operators to produce the same result. See my answer for example. – shmosel Oct 03 '17 at 20:33
  • @shmosel I am still not sure why one has to follow "Ternary expressions should never return boolean literals." rule. – tsolakp Oct 03 '17 at 20:39
  • @shmosel Yes, you are of course right, stupid comment by me. tsokakp he explained why, think about it. – Oleg Oct 03 '17 at 20:39
  • @tsolakp It's not a syntactical rule, just a strong opinion. – shmosel Oct 03 '17 at 20:41
  • Perhaps @shmosel’s rule looks special too you, but in fact *every* compound boolean expression combining a literal value with an expression can be replaced by a simpler expression doing the same. And the rule, to use the simpler variant when there’s a choice, is not even limited to boolean expressions at all. And shouldn’t need an explanation “why one has to follow”. I can always find a more complicated variant, perhaps looking even more 1337 to someone who has never used the ternary operator before. But this should not be the goal of programming. – Holger Oct 04 '17 at 10:37
  • @shmosel I see that your example avoids capturing `size` when it is `null` but I am curious as to what you gain (maintenance or performance wise) by doing it that way? – tsolakp Oct 04 '17 at 14:02
  • @Holger, link to examples perhaps would have been helpful. – tsolakp Oct 04 '17 at 14:03
  • At first, @shmosel’s example avoids repeating the `null` check in every evaluation. Besides that, there’s the minor advantage that the object created for a non-capturing lambda expression will be reused, as explained [here](https://stackoverflow.com/a/27524543/2711488). Regarding the other comment, examples of what? – Holger Oct 04 '17 at 14:33
  • @Holger, thanks. I did update the answer to reflect your comments and shmosels answer. – tsolakp Oct 04 '17 at 15:17