2

I have the following piece of code (the code for TrResponse and TrNode is irrelevant):

public TrResponse from(final TrNode b1, final TrNode b2, final TrNode b3) { final TrResponse resp = new TrResponse(); Stream.of(b1, b2, b3) .filter(Objects::nonNull) .findFirst() .ifPresent(it -> { resp.m1 = it.m1; resp.m2 = it.m2; resp.m3 = it.m3; }); // b1, b2 or b3 can be null // normalize() returns a mutated TrNode resp.b1 = (null != b1) ? b1.normalize() : null; resp.b2 = (null != b2) ? b2.normalize() : null; resp.b3 = (null != b3) ? b3.normalize() : null; return resp; } 

I'm trying to figure it out how to execute the normalize function for all of b1, b2, and b3 (that are eventually non-null) within the same stream operation so I don't have to have those null checks later on (as I would have now). I've tried .map(TrNode::normalize) right before .findFirst(), but it only applies it, eventually, to the first found instance (if any).

Any clues?

5
  • 1
    I don't think there is a need to do this within the stream as the result of normalize are being assigned to different fields and there is no reason to treat them as a stream from a collection as such. Commented Feb 27, 2018 at 16:33
  • 2
    Just an aside this comment is, but is your use of Yoda conditions related to your name being upside-down? Commented Feb 27, 2018 at 17:24
  • 2
    @DodgyCodeException ¿ʇnoqɐ ƃuᴉʞlɐʇ noʎ ǝɹɐ ʇɐɥʍ Commented Feb 27, 2018 at 17:30
  • It's like Mandarin...but worst :p Commented Feb 27, 2018 at 17:38
  • Is the initial value of resp.b[123] different from null? Or, in other words, is there a reason to use resp.b1 = (null != b1) ? b1.normalize() : null; instead of just if(null != b1) resp.b1 = b1.normalize();? Commented Feb 28, 2018 at 10:36

2 Answers 2

1

findFirst is doing exactly what it's supposed to here. It's a short-circuiting terminal operation.

You could write your own collector which just accepts the first element and ignores everything else. collect is not short-circuiting so would process every element. You could then use map as you were trying to do.

class FirstNode { private TrNode node; public void setNode(final TrNode node) { if (this.node == null) { this.node = node; } } public Optional<TrNode> first() { return Optional.ofNullable(node); } } Stream.of(b1, b2, b3) .map(TrNode::normalize) .filter(Objects::nonNull) .collect( Collector.of( FirstNode::new, FirstNode::setNode, (node, node2) -> node ) ) .first() .ifPresent(it -> { resp.m1 = it.m1; resp.m2 = it.m2; resp.m3 = it.m3; }); 

But is this better or more readable than what you have at the moment? Not really. It's also worth noting that you'll be normalizing everything - is that required or desirable?

Sign up to request clarification or add additional context in comments.

2 Comments

But the OP never cared if the result of normalize is null.
Also with this how can we assign to b1,b2,b3 the result of normalize?
1

b1, b2 and b3 are not a collection but individual fields of an object.
How do you want to apply stream operations on?

Just replace this duplicate code :

resp.b1 = (null != b1) ? b1.normalize() : null; resp.b2 = (null != b2) ? b2.normalize() : null; resp.b3 = (null != b3) ? b3.normalize() : null; 

by an helper method.

Personally I would provide specific methods in TrResponse to be able to apply the normalize methods in this way :

resp.setNormalizedOrNull(b1, b2, b3); 

Where setNormalizedOrNull is defined as :

public void setNormalizedOrNull(TrNode b1, TrNode b2, TrNode b3){ this.b1 = getNormalizedOrNull(b1); this.b2 = getNormalizedOrNull(b2); this.b3 = getNormalizedOrNull(b3); } // helper method : private TrNode getNormalizedOrNull(TrNode node){ return null != node ? node.normalize() : null; } 

2 Comments

Yeah, I get it. I have indeed a helper/utility function to wrap and check for null values, but I'm looking for something different. Basically, I'm trying to figure it out (if possible because so far I can't find a way to do it like that) if I can chain two (sort of) terminal operations on the same stream...then I just need to assign the members/fields back to the TrResponse instance.
@ɐuıɥɔɐɯ It should be obvious from the definition of the word 'terminal' that you cannot perform anything after it.