1

I have some Strings and if any one is null, then I need to return null. What is the way to achieve this via Stream? or is there any better way?

protected String toCombinedString(SomeClass shipment) {
        String prefix1 = ofNullable(shipment)
                .map(SomeClass::getBill)
                .map(Bill::getPrefixString)
                .orElse(null);

       String prefix2 = ofNullable(shipment)
                .map(SomeClass::getBill)
                .map(Bill::getPrefixString)
                .orElse(null);

        String number1 = ofNullable(shipment)
                .map(SomeClass::getBill)
                .map(Bill::getNumberString)
                .orElse(null);

        String number2 = ofNullable(shipment)
                .map(SomeClass::getBill)
                .map(Bill::getNumberString)
                .orElse(null);

              ....

        return Stream.of(prefix1, number1, prefix2, number2...)
                .filter(Objects::nonNull)
                .reduce((a, b) -> a + "-" + b)
                .orElseGet(String::new);
    }

Example output combinations:

Only pass case is, every String should be non-empty or non-null, else return null

  • String, String, String, String -> String-String-String-String
0

6 Answers 6

2

You seem to perform a ternary operation such as:

return prefix == null || number == null ? null : prefix + "-" + number;

After the edit in the question, and the condition that only all such String type of attributes from the Bill entity would be considered in the result. You can formulate a method to extract the String from all attributes as:

protected String toCombinedString(SomeClass shipment) {
    return Optional.ofNullable(shipment)
            .map(SomeClass::getBill)
            .map(bill -> extractAttributes(bill, Bill::getNumberString, Bill::getPrefixString)) // use this further
            .orElse(null);
}

private String extractAttributes(Bill entity, Function<Bill, String>... mappers) {
    List<String> attributes = Arrays.stream(mappers)
            .map(function -> function.apply(entity))
            .collect(Collectors.toList());
    return attributes.stream().anyMatch(s -> s == null || s.isEmpty()) ?
            null : String.join("-", attributes);
}
Sign up to request clarification or add additional context in comments.

3 Comments

@naman I have updated the question. If I have to traverse 4/5 levels down and if any string is empty, then i have to return null. For one or two String ternary operation is sufficient. Any inputs on more Strings?
@ZafrullahSyed my answer should fit your use case and uses ternary without complexicity
@ZafrullahSyed For multiple such arguments, you might want to collect them into a collection and validate the elements collected for the predicate that you're willing to and return accordingly. The update in the question doesn't really make it clear what your exact use case is. If the predicate is supposed to be the same for all variables, look for allMatch, otherwise, define better in the question what exactly are you looking forward to.
1

You can use

List<String> list = Arrays.asList(prefix1, number1, prefix2, number2...);
return list.contains(null)? null: String.join("-", list);

Using a Stream would not improve anything. The costs of the temporary storage are the same, as Stream.of(…) uses a temporary array just like Arrays.asList(…) or varargs methods in general.

But considering that each string is the result of a complex operation, the complexity or simplicity of the one final statement isn’t really relevant.

I’d rather consider:

String prefix1 = your
                 long
                 complicated
                 operation;
if(prefix1 == null) return null;

String prefix2 = second
                 long
                 complicated
                 operation;
if(prefix2 == null) return null;

String number1 = third
                 long
                 complicated
                 operation;
if(number1 == null) return null;

String number2 = fourth
                 long
                 complicated
                 operation;
if(number2 == null) return null;

…

return String.join("-", prefix1, number1, prefix2, number2 …);

Comments

0

You can utilise the Count function of Java 8's Streams:

long i = list.stream()
             .filter(str -> str == null)
             .count();

The above code will count any null values in the supplied List of Strings.

You can then check if the count is greater than 0. If so, then return null.

if (i > 0) {
   return null;
}

2 Comments

Not sure your code compiles: str -> str = null. It should be str -> str == null
Yes, correct, thanks for the update, I always forget that extra =
0

I don't really see your use case for this, however I think that this code snipper should work for you. The idea is to throw an exception instead of the optional orElse returning a null. Your can implement code like that:

protected String toCombinedString(SomeClass shipment) {
   try {
        String prefix = ofNullable(shipment)
                .map(SomeClass::getBill)
                .map(Bill::getPrefixString)
                .orElseThrow(NullPointerException::new));

        String number = ofNullable(shipment)
                .map(SomeClass::getBill)
                .map(Bill::getNumberString)
                .orElseThrow(NullPointerException::new));
        .....

        return Stream.of(prefix, number, prefix1, number1....)
                .reduce((a, b) -> a + "-" + b);
   } catch(NullPointerException e) {
        return null;
   }
}

You could also wrap the try catch outside of that function for clarity but this logic should work and would optimize the performance as the execution stops at the first encountered null.

1 Comment

@Naman you are right I adjusted my answer with a different logic
0

I think streams would complicate the thing, they should be used when dealing with collections or aggregations of data.

This is an example without using streams:

public class Main {

    static class FieldsContainer {
        public String field1;
        public String field2;

        public FieldsContainer(String field1, String field2) {
            super();
            this.field1 = field1;
            this.field2 = field2;
        }

        public String getField1() {
            return field1;
        }
        public void setField1(String field1) {
            this.field1 = field1;
        }
        public String getField2() {
            return field2;
        }
        public void setField2(String field2) {
            this.field2 = field2;
        }

    }

    static class SomeClass {
        FieldsContainer container;

        public SomeClass(FieldsContainer container) {
            super();
            this.container = container;
        }

        public FieldsContainer getContainer() {
            return container;
        }

        public void setContainer(FieldsContainer container) {
            this.container = container;
        }

    }

    public static void main(String[] args) {
        SomeClass shipment = new SomeClass(new FieldsContainer("1", "2"));
        test(shipment);
    }

    public static String test(SomeClass shipment) {
        FieldsContainer fieldsContainer = shipment.getContainer();
        if (fieldsContainer == null) {
            return null;
        }

        String field1 = fieldsContainer.getField1();
        if (field1 == null) {
            return null;
        }

        String field2 = fieldsContainer.getField2();
        if (field2 == null) {
            return null;
        }

        return field1 + " - " + field2;
    }

}

Comments

0

If you have only two strings as was in the first version of your question, using ternary operation is more efficient way to solve your problem: return prefix == null || number == null ? null : prefix + "-" + number;

If you have more items use Streams:

boolean hasNull = Stream.of(prefix, num).anyMatch(Objects::isNull);

return hasNull ? null : Stream.of(prefix, num).collect(Collectors.joining("-"));

1 Comment

There is no point in doing .filter(Objects::isNull) before .anyMatch(Objects::isNull).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.