0

Assuming that this usage is logically (i.e. it calculates the desired boolean value) correct:

boolean matches = someObjectList
            .stream()
            .anyMatch(myObjType -> !myObjType.getSomeStatusString().equals("someStatus"));

how would that compare to:

boolean matches = someObjectList
            .stream()
            .map(myObjType -> myObjType.getSomeStatusString())
            .anyMatch(status -> !status.equals("someStatus"));

Is one form objectively better than the other? Is there a significant difference in the bytecode? Is one an anti-pattern? Is there some Java Optimizer that makes one better than the other? I am not interested in opinion based differences such as the first is more readable, as that may differ from reader to reader....

JoeG
  • 7,191
  • 10
  • 60
  • 105
  • 1
    The post is asking for an opinion, therefore off-topic. – Turing85 Jan 10 '20 at 18:33
  • 1
    using of `map` makes it clear and readable. – Hadi J Jan 10 '20 at 18:34
  • 4
    Performance-wise, they should be the same, or maybe the first one marginally faster by doing slightly fewer operations. The first reads easier to me. The second would read easier to the eyes if it used method references, but it can't. – 9000 Jan 10 '20 at 18:34
  • 1
    If you wantt less lines of code, take the first one. If you want to decrease reading time for other developers, prefer the second one. But it's totally depending on coding conventions or just your opinion. – deHaar Jan 10 '20 at 18:36
  • @Turing85, I'm NOT looking for opinions on which is better, I want to know if anyone KNOWS why one is superior to the other. There could be lots of categories, so I didn't want to narrow it down to one area. – JoeG Jan 10 '20 at 18:41
  • 1
    @JoeG They are functionally equivalent. – Michael Jan 10 '20 at 18:42
  • 4
    @JoeG you did not define *superiority*. Without any metric, this question is opinion-based. If you want to know pros and cons of each approach in comparision, then ask for that. – Turing85 Jan 10 '20 at 18:43
  • 1
    One-liner: `!someObjectList.stream().map(Foo::getSomeStatusString).allMatch("someStatus"::equals)` – Michael Jan 10 '20 at 18:48
  • @Michael where are you referring to? – deHaar Jan 10 '20 at 18:49
  • @Michael maybe, but that wasn't my comment... – deHaar Jan 10 '20 at 18:51
  • 1
    @9000 "if it used method references, but it can't" It can – Michael Jan 10 '20 at 18:51
  • I think the performance difference is insignificant for this particular operation – Samuel Owino Jan 10 '20 at 23:43
  • 1
    I agree with @Unknown the second one is going to create many objects that are not needed – user1609258 Jan 14 '20 at 00:58
  • There is no temporary list, it's a stream; and if those strings don't already exist, they have to be created when the method is called, whenever that is. – kaya3 Jan 14 '20 at 19:18

1 Answers1

0

I stumbled across the answer I was looking for in this SO question

The selected answer (discussing Garbage Collection) says in part:

In implementations like the Hotspot JVM, each thread uses a thread local allocation buffer (TLAB) for new objects. Once its TLAB is full, it will fetch a new one from the allocation space (aka Eden space). If there is none available, a garbage collection will be triggered. Now it’s rather unlikely that all threads hit the end of their TLAB right at the same time. So for the other threads which still have some space in their TLAB left at this time, it would not make any difference if they had allocated some more objects still fitting in that remaining space.

The perhaps surprising conclusion is that not every allocated object has an impact on the garbage collection, i.e. a purely local object allocated by a thread not triggering the next gc, could be entirely free.

My conclusion is that the creation of the "extra" objects in the second approach comes at no objective cost. Thus I do not believe there to be any objective benefit of one form over the other.

Community
  • 1
  • 1
JoeG
  • 7,191
  • 10
  • 60
  • 105
  • What "extra" objects are created? – kaya3 Jan 14 '20 at 19:16
  • I believe the .map operation will need its own String Object, it will not be able to use a reference to the String the way the "get" call will (i.e the code without the .map). But not knowing this is the motivation as to why I asked in the first place. – JoeG Jan 16 '20 at 14:38
  • Well, the `getSomeStatusString` method is called once per object either way, so the same string objects are created (unless they already exist before `getSomeStatusString` is called) either way. – kaya3 Jan 16 '20 at 14:41
  • Yes, that 'get' method is intended as a POJO get so naturally those Strings already exist – JoeG Jan 16 '20 at 14:56
  • Then no new String objects are created either way. – kaya3 Jan 16 '20 at 15:18
  • 1
    So, it’s the correct conclusion for the slightly wrong reasons. If there were additional string creations, they made a difference, as a per-element string creation can be expensive (a string creation implies copying the character contents which makes it more expensive than plain object creation). However, there is no per-element string creation in the second variant but a few more objects created for the entire stream, a `Function` instance and Stream implementation specific objects to remember that the `map` operation has been chained. These are exactly the objects that do not matter at all. – Holger Apr 07 '21 at 11:54