4

I am modifying an object using java-8

users.stream().filter(u -> u.count > 0).forEach(u -> u.setProperty("value"))

However, I want to understand if any object was modified or not... i.e, I want a boolean return value, while this is void.

Any way to do it?

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Tanvi Jaywant
  • 375
  • 1
  • 6
  • 18

3 Answers3

6

If I get you correctly, you want to know whether there were any matches while performing the operation. You could simply use two statements.

boolean anyMatch = users.stream().anyMatch(u -> u.count > 0);
if(anyMatch) users.stream().filter(u -> u.count > 0).forEach(u -> u.setProperty("value"));

Since anyMatch stops at the first matching element, there would be redundant work only if there is a long prefix of non-matching elements before the first match.

If that’s a concern, you could use

Spliterator<User> sp = users.stream().filter(u -> u.count > 0).spliterator();
boolean anyMatch = sp.tryAdvance(u -> u.setProperty("value"));
sp.forEachRemaining(u -> u.setProperty("value"));

instead.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 2
    read the question -> hey perfect case for a spliterator -> wrote it -> read your answer :| -> deleted mine -> upvoted yours – Eugene May 17 '18 at 19:42
3

Since this is a consuming operation, it can only ever be used with methods that don't actually return anything back; that is, using forEach ensures a terminal operation in which you don't get a return value back.

If you want to validate that the property is set the way you want it to be, you'd have to check the elements again.

users.stream().filter(u -> u.count > 0)
              .allMatch(u -> u.getProperty().equals("value"));

Although this speaks more to paranoia than anything else; unless setProperty has some other side effect which isn't exposed here, then the setter should always set the value. I'd write the above in a unit test for validation purposes, but not in production code.

Makoto
  • 104,088
  • 27
  • 192
  • 230
  • No, actually [you don't have to check again](https://stackoverflow.com/a/50396988/256196). – Bohemian May 17 '18 at 17:11
  • 3
    @Bohemian: At a minimum I would say checking in this fashion would be best left to a unit test anyway, especially given that `peek` is really [only meant for debugging](https://stackoverflow.com/q/33635717/1079354). – Makoto May 17 '18 at 17:33
2

Add a call to peek():

AtomicBoolean modified = new AtomicBoolean();
users.stream()
    .filter(u -> u.count > 0)
    .peek(u -> modified.set(true))
    .forEach(u -> u.setProperty("value"))

If any elements make it through the filter, modified.get() will return true.

The use of AtomicBoolean (or something similar) is required because references used in lambdas must be effectively final.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 2
    If you are going that route (using `peek`), why not `boolean modified = users.stream().filter(u -> u.count > 0).peek(u -> u.setProperty("value")).count() > 0;`? That doesn’t need an `AtomicBoolean`. On the other hand, if you are using `AtomicBoolean`, you don’t need `peek`: `users.stream().filter(u -> u.count > 0).forEach(u -> { modified.set(true); u.setProperty("value"); });` – Holger May 17 '18 at 17:14
  • @holger because adding `peek()` to set a flag leaves OP's existing code unchanged - it's easy to add and remove. It doesn't embed the modification detection in with the main code - see [*separation of concerns*](https://en.wikipedia.org/wiki/Separation_of_concerns). It's clear what each stream method is doing and why it's being called. `peek()` is the right method to use in this case. – Bohemian May 17 '18 at 17:27
  • 3
    …except that the operation within `peek` will stop working if you remove the terminal operation, hence, there is no real “separation of concerns”, but a not-so-obvious dependency. – Holger May 17 '18 at 18:18
  • @holger it’s good that the peek won’t work if you take away the terminal operation, because it’s the terminal operation OP wants to count. There *is* a separation of concerns: with peek, you remove the count without touching or affecting any of the other code. Actually, formatted like I have it you can remove it from the stream by commenting out the peek line - how simple is that? – Bohemian May 18 '18 at 02:48