Conditionally transforming Stream elements when predicate matches - java

I have a stream and want to apply a method only if a predicate matches.
E.g. I want to process a stream and replace all nulls by a default value. What is the best way to accomplish this?

You should just use a map value
data.stream()
.map(v -> v == null ? defaultValue : v)
... // do whatever you need to do with it.
EDIT
If you need to do this a lot you could create a Function to do it for you.
public class DefaultValue<T> extends Function<T, T> P{
private final T t;
public DefaultValue(T t){
this.t. = t;
}
public T apply(T t) {
return t == null ? this.t : t;
}
}
data.stream()
.map(new DefaultValue(someValue));
// Do what you need to do

If you are looking to preserve the original values for items that do not match your filter, use map with ternary logic:
Items that do not pass filter are returned as-is
Items that pass the filter get transformed
Here is an example:
Stream<String> stream = Arrays.stream(
new String[]{"quick", null, "brown", "fox", null, "jumps"}
);
List<String> res = stream
.map(s -> s != null ? s : "<EMPTY>")
.collect(Collectors.toList());
for (String s : res) {
System.out.println(s);
}
Filtering logic is embedded in the conditional expression inside map:
s -> s != null ? s : "<EMPTY>" // Using default values for null strings
Demo.

Related

How to optimise this filtering of list in Java

I have the following code:
private List<String> validate(StartValue start, List<String> colors, Entity entity) {
if (!CollectionUtils.isEmpty(colors)) {
return colors.stream()
.filter(color -> ValidationUtil.getColorfulValues(start, color.getRGBValue()).isEmpty() ||
(!ValidationUtil.getColorfulValues(start, color.getRGBValue()).isEmpty() &&
ValidationUtil.getColorfulValues(start, color.getRGBValue()).contains(entity.getColor())))
.collect(Collectors.toList());
}
return colors;
}
Here ValidationUtil.getColorfulValues is getting called thrice for each value in the list. Is there a way to optimize the filter so that we can save the value of the call?
If you would have not represented the code as a lambda expression, but as a block statement you would have been able to simplify it further.
.filter(color -> {
List<String> colourFulValues = ValidationUtil.getColorfulValues(start,
color.getRGBValue());
return colourFulValues.isEmpty() || colourFulValues.contains(entity.getColor())
}
Of course, you could abstract the block as a Predicate of its own depending on its usage. The type of the Predicate would be the type you have chosen to represent the color. Currently, in your question, for example, if it's a java.lang.String, where is the method color.getRGBValue() associated from?
You can use a method reference:
private List<String> validate(StartValue start, List<String> colors, Entity entity) {
if (!CollectionUtils.isEmpty(colors)) {
return colors.stream()
.filter(this::filter)
.collect(Collectors.toList());
}
return colors;
}
private boolean filter(String color) {
var rgbVal = color.getRGBValue();
var cv = ValidationUtil.getColorfulValues(start, rgbVal);
boolean empty = cv.isEmpty();
return empty || (!empty && cv.contains(entity.getColor()));
}
Note that I use var since it is not clear to me what color.getRGBValue() returns and also what ValidationUtil.getColorfulValues() returns.
Guessing that CollectionUtils.isEmpty considers null elements non-existing.
For the filter condition evaluating ValidationUtil.getColorfulValues(start, color.getRGBValue())) only once requires mapping to it. But one needs to conserve the color too, to collect it in a list. So I introduced a record ColorWithValues. The record class is intended for such kind of uses.
|| is a short-circuit operator and does not need a negation of its first argument.
So:
private List<String> validate(StartValue start, List<String> colors, Entity entity) {
record ColorWithValues(String color, Set<String> values) {}
return colors.stream()
.filter(Objects::nonNull)
.map(c ->
new ColorWithValues(c,
ValidationUtil.getColorfulValues(start, c.getRGBValue())))
.filter(cv -> cv.values.isEmpty() ||
cv.values.contains(entity.getColor()))
.map(cv -> cv.color)
.collect(Collectors.toList());
}

Convert one Optional<List<Object>> to another Optional<List<Object>> in Java

How can I convert Optional List object from one type to another, for an example
Optional<List<ProductMultipleOptionViewModel>> productOptionType1 // One type
Optional<List<ProductMultipleOption>> productOptionType2 // Other type
ProductMultipleOptionViewModel
Type 1
#Introspected
public record ProductMultipleOptionViewModel(
ProductOptionViewModel productOption,
String optionName) {
}
Type 2
#Introspected
public record ProductMultipleOption(
ProductOptionViewModel productOption,
String optionName) {
}
I want to convert from Optional<List<ProductMultipleOption>>to other Optional<List<ProductMultipleOptionViewModel>>. I tried the below code
Optional<List<ProductMultipleOptionViewModel>> conveertItem = Optional.ofNullable(product.getProductMultipleOption())
.orElseGet(null)
.stream()
.map(option -> {
return new ProductMultipleOptionViewModel(
ProductOptionViewModel.valueOf(//Access the option value//), //access the option value//
);
})
.collect(Collectors.toList());
With the above code, I am not able to access the option value inside map method
If product.getProductMultipleOption() is null return null or empty list.
You should rarely use Optional and Collections (like List or Set) together. Instead you should work with empty Collections. Also keep in mind that Optionals should not really be used for conditional logic, but rather as return values.
Either using a normal if statement:
List<ProductMultipleOptionViewModel> conveertItem = new ArrayList<>();
if (product.getProductMultipleOption() != null) {
for(ProductMultipleOption option : product.getProductMultipleOption()) {
conveertItem.add(new ProductMultipleOptionViewModel(
ProductOptionViewModel.valueOf(option)
));
}
}
Another variant:
List<ProductMultipleOption> list = product.getProductMultipleOption();
if (list == null) {
list = Collections.emptyList();
}
List<ProductMultipleOptionViewModel> conveertItem = new ArrayList<>();
for(ProductMultipleOption option : list) {
conveertItem.add(new ProductMultipleOptionViewModel(
ProductOptionViewModel.valueOf(option)
));
}
Or if you really want to use Streams and Optionals (matter of taste):
List<ProductMultipleOptionViewModel> conveertItem = Optional.ofNullable(product.getProductMultipleOption())
.map(List::stream)
.orElseGet(Stream::empty)
.map(option -> new ProductMultipleOptionViewModel(
ProductOptionViewModel.valueOf(option)
))
.collect(Collectors.toList());
Now that's a lot of code for simply converting a nullable List. So why not return an empty List in the first place? Change product.getProductMultipleOption() to something like this:
public List<ProductMultipleOption> getProductMultipleOption() {
List<ProductMultipleOption> list = ...; // your current logic for getting the list
return list == null ? Collections.emptyList() : list;
}
That way you never have to worry about null checks. Because you're simply working with an empty collection wherever you're calling getProductMultipleOption().
It helps to think about dealing with nulls/empty optionals separately from dealing with the list. The code below deals with nulls using the Optional.map() method, which returns an empty optional (of the appropriate return type) if the given argument is empty; otherwise, it applies the mapping function on the list.
Optional<List<ProductMultipleOptionViewModel>> convertedItem =
Optional.ofNullable(product.getProductMultipleOption())
.map(list -> list.stream()
.map(option -> new ProductMultipleOptionViewModel(
option.productOption(),
option.optionName()))
.collect(Collectors.toList()));
Might not be the best way to do whatever you're doing... but to answer your question if you're trying to work with what you've got and keep it minimal:
private List<ProductMultipleOption> getProductOptionViewModelList() {
/* simulating return of a list that could be null. */
return null;
}
private Optional<List<ProductMultipleOption>> getProductMultipleOptionNull() {
/* simulating return of an optional list. */
return Optional.empty();
}
private static class ProductOptionViewModel { }
public record ProductMultipleOptionViewModel(
ProductOptionViewModel productOption,
String optionName) {
}
public record ProductMultipleOption(
ProductOptionViewModel productOption,
String optionName) {
}
/*
Create your own methods to convert the models.
Replace the variables with whichever method is available to get the name:
(inputOption.productOption, inputOption.optionName)
(inputOption.productOption(), inputOption.optionName())
. (inputOption.getProductOption(), inputOption.getOptionName())
*/
private ProductMultipleOptionViewModel convertToMultipleOptionViewModel(
ProductMultipleOption inputOption) {
return new ProductMultipleOptionViewModel(
inputOption.productOption,
inputOption.optionName);
}
private ProductMultipleOption convertToMultipleOption(
ProductMultipleOptionViewModel inputOption) {
return new ProductMultipleOption(
inputOption.productOption,
inputOption.optionName);
}
/*
If the list you're getting is Optional<List<ProductOptionViewModel>>
and you want List<ProductMultipleOptionViewModel>
*/
List<ProductMultipleOptionViewModel> convertedFromOptionalList =
getProductMultipleOptionNull()
.stream()
.flatMap(Collection::stream)
.map(this::convertToMultipleOptionViewModel)
.toList();
/*
If the list you're getting is List<ProductOptionViewModel>
and you want List<ProductMultipleOptionViewModel>
*/
List<ProductMultipleOptionViewModel> convertedFromNullableList = Optional
.ofNullable(getProductOptionViewModelList())
.stream()
.flatMap(Collection::stream)
.map(this::convertToMultipleOptionViewModel)
.toList();
/*
If for some reason you're trying to get the list as
Optional<List<ProductOptionViewModel>> you can wrap
them with Optional.of() :
*/
Optional<List<ProductMultipleOptionViewModel>> convertedFromOptionalList = Optional
.of(Optional.ofNullable(getProductOptionViewModelList())
.stream()
.flatMap(Collection::stream)
.map(this::convertToMultipleOptionViewModel)
.toList());
Optional<List<ProductMultipleOptionViewModel>> convertedFromNullableList = Optional
.of(getProductMultipleOptionNull()
.stream()
.flatMap(Collection::stream)
.map(this::convertToMultipleOptionViewModel)
.toList());

Handling Null pointer in the given Java stream based code

I am having the below given data structure.
In the given code block, Im trying to get Object Dc from the given HashMap dealCommits.
The piece of code will work if the map has got the object associated with the given key (cNumber) is available.
My problem is how to handle the null pointer in line (//5) when the given key is not found in the map.
It will be a great help if somebody can throw some light to amend this stream based code to handle the exception.
public class Dc {
private String cNumber;
private Optional<List<pTerms>> pTerms;
}
public class Dd {
private String cParty;
//Dc:cNumber is the Key
private Optional<Map<String,Dc>> dealCommits;
}
public class PTerms {
private String pType;
}
public String check(String tMonth,String cNumber,Dd dDetails)
{
Optional<DealPricingTerms> dealPricingTerms = dDetails
.getDealCommits().get()
.get(cNumber)
.getPTerms().get().stream() //5
.filter(dealPricingTerm ->
tMonth.equals(dealPricingTerm.getDeliveryPeriod()))
.findFirst();
return dealPricingTerms.isPresent()? "Success" : "failed";
}
You shouldn't call get() on your Optionals - and when you use Optional.map you have a nice way to wrap a null result from a Map get into another Optional:
Optional<PTerms> dealPricingTerms = dDetails
.getDealCommits().map(c -> c.get(cNumber))
.flatMap(dc -> dc.getPTerms())
.map(l -> l.stream())
.flatMap(s ->
s.filter(dealPricingTerm ->
tMonth.equals(dealPricingTerm.getDeliveryPeriod()))
.findFirst());
You have to add filter to check null before your condition filter.
.filter(Objects::nonNull)
for example,
List<String> carsFiltered = Optional.ofNullable(cars)
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull) //filtering car object that are null
.map(Car::getName) //now it's a stream of Strings
.filter(Objects::nonNull) //filtering null in Strings
.filter(name -> name.startsWith("M"))
.collect(Collectors.toList()); //back to List of Strings

How return null by using Stream API?

So, I have an ArrayList of autogenerated strings. I want to find the first element that contains some character or else if there is no one element that matches this filter, to apply another filter. In another way, I want to return null object.
So I write this lambda expression:
str.stream()
.filter(s -> s.contains("q"))
.findFirst()
.orElseGet(() -> str.stream()
.filter(s -> s.contains("w"))
.findFirst()
.orElseGet(null))
But if there is no one element that matches this two filters I will have NullPointerException. How, can I get something like: return null?
Unless I'm missing something obvious, simply .orElse(null) instead of the last orElseGet. orElseGet accepts a Supplier and you pass a null, calling get() on a null reference, well...
An additional, simpler approach: you can filter on strings containing q or w, then sort to move those containing q first, find first, return null if the optional is empty:
str.stream()
.filter(s -> s.contains("q") || s.contains("w"))
.sorted((s1, s2) -> s1.contains("q") ? -1 : 1 )
.findFirst()
.orElse(null);
.sorted((s1, s2) -> s1.contains("q") ? -1 : 1 ) will move the strings containing "q" first. Since the stream has been filtered only on values containing either q or w, then returning null will only happen no element is retained.
The problem is Optional::orElseGet(null) which accepts a Supplier<T> and throws the NullPointerException.
Use the Optional::orElse(null) which accepts T which is String in your case.
The following code works:
List<String> str = Arrays.asList("a", "b", "c");
String output = str.stream()
.filter(s -> s.contains("q"))
.findFirst()
.orElseGet(() -> str.stream()
.filter(s -> s.contains("w"))
.findFirst()
.orElse(null)); // <-- HERE
System.out.println(output); // Prints null
Alternatively, use the Optional::orElseGet(Supplier<? extends T> other) to return null. The Supplier itself must be not null:
.orElseGet(() -> null));
orElseGet expects a Supplier, you need to pass a value, which is the case for orElse.
I wouldn't use Stream API here, one iteration is enough to solve the problem:
String wString = null;
for (String s : str) {
if (s.contains("q")) return s;
if (s.contains("w") && wString == null) wString = s;
}
return wString;

RxJava: dynamically create Observables and send the final resut as Observable

I am using RxJava in which I want to dynamically create a number of Observables based on some condition. Once I'm done with creating, I want to do some processing on the different values returned by the observables and then send as a single Observable to which I can subscribe on. Here is how my code is :
List<String> valueList = ....
List<Observable<String>> listOfObservables = new ArrayList<Observable<String>>();
for(int i =; i <valueList.size(); i++){
listOfObservables.add(new SomeClass.doOperation(valueList(i)));
// SomeClass.doOperation will return an Observable<String>
}
return Observable.merge(listOfObservables);
But here , I want to do some operation on the values emitted by different Observables in the listOfObservable and finally return it as a single Observable<String>
Like in Observable.zip() , I can do this like
return Observable.zip(observable1, observable2, (string1, string2) -> {
// joining final string here
return string1 + string2;
But I know the number of arguments here. Please let me know how I can achieve this.
Use the zip overload that takes a variable number of arguments, it has a signature of
<R> Observable<R> zip(Iterable<? extends Observable<?>> ws,
FuncN<? extends R> zipFunction)
Example usage:
List<String> valueList = ....
return Observable.from(valueList)
.map(string -> SomeClass.doOperationThatReturnsObservable(string))
.toList()
.flatMap(listOfObs -> Observable.zip(listOfObs, (Object[] results) -> {
// do something with the strings in the array.
return Arrays.stream(results)
.map(Object::toString)
.collect(Collectors.joining(","));
}));

Categories