Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cleanly process java 8 stream "findFirst()" result even if empty

One area that I often finding confusing with java 8 streams is when an intermediate result can be empty, and you need to take alternate paths if it's empty or not empty.

For instance, if I have code like this:

String pymtRef = defaultValue;
Optional<PaymentTender> paymentTender = paymentTenders.stream()
        .filter(pt -> (pt.getFlag() == Flag.N || pt.getFlag() == null)).findFirst();
if (paymentTender.isPresent()) {
    pymtRef = paymentTender.get().getId();
}
return pymtRef;

I would like to figure out how to remove the conditional block and do this in a single stream.

If I simply call ".map" on the filter result, that can work if it found a matching entry. If not, I get a NoSuchElementException.

I might instead use "ifPresent()", but the return type of that is "void".

Is there any way to make this cleaner?

Update:

The solution using "orElse()" works fine.

The entire method now looks something like this:

public String getPaymentReference(OrderContext orderContext) {
    List<PaymentTender> paymentTenders = getPaymentTenders(orderContext);
    if (paymentTenders.size() == 1) {
        return paymentTenders.get(0).getId();
    }
    return paymentTenders.stream()
            .filter(pt -> (pt.getAutoBill() == AutoBill.N || pt.getAutoBill() == null))
            .findFirst().map(pt -> pt.getId()).orElse(DEFAULT_VALUE);
}

Can you think of a way to include the first conditional in the stream without making it more complex?

like image 333
David M. Karr Avatar asked Dec 11 '18 22:12

David M. Karr


People also ask

What happens if stream is empty Java?

Return Value : Stream empty() returns an empty sequential stream. Note : An empty stream might be useful to avoid null pointer exceptions while callings methods with stream parameters.

How do I filter NULL values in Java 8 stream?

We can use lambda expression str -> str!= null inside stream filter() to filter out null values from a stream.

How do I create an empty stream in Java 8?

stream(new int[]{}). count()); prints zero. Any stream created from a collection (like a List or Set ) with zero elements can return an empty stream; for example: new ArrayList<Integer>(). stream() returns an empty stream of type Integer .


2 Answers

Calling get() straight after map will yield an exception if the Optional has an empty state, instead call orElse after map and provide a default value:

paymentTenders.stream()
            .filter(pt -> (pt.getFlag() == Flag.N || pt.getFlag() == null))
            .findFirst()
            .map(PaymentTender::getId)
            .orElse(someDefaultValue);

Edit:

As for:

Can you think of a way to include the first conditional in the stream without making it more complex?

No, this is better the way you've done it. it's more readable and easier to follow.

introducing any type of logic to make it into one pipeline (if possible) will just end of being complex and hence harder to follow and understand.

like image 137
Ousmane D. Avatar answered Oct 04 '22 03:10

Ousmane D.


You can do it in one statement via

public String getPaymentReference(OrderContext orderContext) {
    List<PaymentTender> paymentTenders = getPaymentTenders(orderContext);
    return paymentTenders.stream()
        .filter(paymentTenders.size() == 1? pt -> true:
                pt -> pt.getAutoBill() == AutoBill.N || pt.getAutoBill() == null)
        .findFirst().map(PaymentTender::getId).orElse(DEFAULT_VALUE);
}

Note that this will not repeat the evaluation of the paymentTenders.size() == 1 for every element, but use a different function, depending on the state. When the condition is fulfilled, pt -> true will accept any element, which will result in the sole element being accepted as intended. Otherwise, the ordinary predicate, pt -> pt.getAutoBill() == AutoBill.N || pt.getAutoBill() == null is used.

like image 37
Holger Avatar answered Oct 04 '22 03:10

Holger