Elegant way to throw exception on empty stream - java

I am fetching list of clients. Then I map enitity to dto and return result. I would like to throw exception on empty client list, but also I want to avoid if statement in code.
I can wrap list in optional but I believe there is more elegant solution.
Set<ClientDto> clients = Optional.of(repo.findByNumber(number))
.filter(CollectionUtils::isNotEmpty)
.orElseThrow(() -> new NotFoundException())
.stream()
.map(client -> new ClientDto(client.getName()))
.collect(Collectors.toSet());
Is there any cleaner solution? Because there is few usless chains in my code. I am even now starting to think that pure if would be more readable.

I don't know how you can do it with pure streams but you could define a method which returns a stream of the list and throw an exception if its empty.
private <T> Stream<T> throwIfEmpty(List<T> list) {
if(list.isEmpty()) {
throw new IllegalArgumentException("List must not be empty");
}
return list.stream();
}
then you could use it the following way:
List<Client> clients = throwIfEmpty(repo.findByNumber(number))
.map(client -> new ClientDto(client.getName()))
.collect(Collectors.toSet());
maybe this solution makes you happy :)

Related

Is it better filter non Null objects with map operation or return Stream.empty() on flatMap operation?

I'm really curious about this simple performance/best_use/best_practice related case scenario:
If I have this simple snippet:
List<String> list = Arrays.asList("Hello1", "Hello2", "Hello3", "Jhon", "Doe", "Hello4");
list.stream()
.map(s -> {
if (s.contains("Hello")) {
return "World";
}
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
OR
list.stream()
.flatMap(s -> {
if (s.contains("Hello")) {
Stream.of("World");
}
return Stream.empty();
})
.collect(Collectors.toList());
NOTE: I know that maybe Map the String just to convert it to another String doesn't make much sense, but that is just for the example representation purposes, it could be a pojo or an integer or anything else.
Which one will perform better? or which would be the best option?
I'm trying to understand which is the better option in cases in which some conditional appears in the logic when we use streams chains.
Thank you.
I wouldn't create and wrapper like another Stream for avoiding nulls, nor return the null value for non matching strings, you can simply filter the strings having Hello word and then use map for value modification
list.stream()
.filter(s->s.contains("Hello"))
.map(s -> "world")
.collect(Collectors.toList());

How to throw an exception properly when do Flux processing?

Existing code that I have:
private Flux<Integer> testGetFluxTestData() {
return Flux.just(new TestData(1), new TestData(2))
.collectList()
.map(list -> list.stream()
.map(TestData::getId)
.collect(Collectors.toList()))
.flatMapMany(Flux::fromIterable);
}
I want to enrich existing code and throw an exception when some not allowed data received, I made the following changes:
private Flux<Integer> testGetFluxTestData2() {
return Flux.just(new TestData(1), new TestData(2))
.collectList()
.map(list -> {
return !list.contains(new TestData(1)) ?
list.stream()
.map(TestData::getId)
.collect(Collectors.toList()) :
Flux.error(new IllegalTestDataException("illegal test data 1"));
})
.flatMapMany(Flux::fromIterable);
}
but my implementation even noncompilable due to the following line:
Flux.error(new IllegalTestDataException("illegal test data 1"));
Could you please suggest, how to handle exception throwing for my particular scenario?
You are attempting to map from a List<TestData> to either a List<Integer> or a Flux<?> (error), which makes the desired result type ambiguous. Returning a reactive type in a mapping function is generally not desired (you'd want to do that in a flatmapping function).
(side note: even if you were in a flatMap, it wouldn't work either because at that point you're in Mono API due to collectList, so Mono.flatMap expects a Mono result to the Function).
Note that the map operator catches exceptions from the lambda and turn them into an onError signal, so technically you could replace the Flux.error with a throw.
Otherwise, you'd need to turn the map into a flatMap and the Flux.error into a Mono.error, for the reasons stated above.

Java stream get single element from collection

I am using a non stream way to get single element from collection.
List<MyCustomClass> list = OtherObject.getMyList();
if (list.size() != 1) {
throw new RuntimeException();
}
MyCustomClass customClass = list.get(0);
Instead of this multi liner approach, is there some way to achieve this via streams?
You can use reduce(accumulator) and orElseThrow(exceptionSupplier) to ensure the stream produces exactly one result.
MyCustomClass customClass = list.stream()
.reduce((a,b) -> { throw new RuntimeException("Too many values present"); })
.orElseThrow(() -> { throw new RuntimeException("No value present"); });
I was looking for a version with a single collect statement, although it turned out not as concise or elegant as the solution by Andreas. It uses an implementation of Collector that accumulates to a one-element list, while the combiner raises an exception if we have more than one element; the finisher raises an exception when the list is empty.
list.stream().collect(
Collector.of( ArrayList::new,
(a, t) -> { if (!a.isEmpty())
throw new RuntimeException();
a.add(t); },
(a, b) -> { throw new RuntimeException(); },
a -> { if( a.isEmpty() )
throw new RuntimeException();
return a.get(0);} );
You could try returning an optional from findFirst() or findAny().
List<String> strings = new ArrayList<>();
Optional<String> maybeFirst = strings.stream().findFirst();
// we now have an optional, lets force a value
String value = maybeFirst.orElseThrow(IllegalArgumentException::new);
// if there isn't a value, we'll throw an illegal argument exception.
This can collapsed into the following.
String value = strings.stream()
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("There must be at least one string."));
Hope that helps.

Getting Incompatible types error while trying to map a list

I have a FeeAccount list that I would like to fill. I want to use .stream.map() to get it done. What I've managed to do is to make a method that would map my list and return it. I've written this code using some other examples I have found online. My problem is that somehow it returns a list that is incompatible with List.
I am getting an error: Incompatible types. Required List but 'map' was inferred to Stream: no instance(s) of type variable(s) R exist so that Stream conforms to List
As I understand the problem is with the part where I use collect(Collectors.toList()). But I am not sure. I don't even clearly understand what the error message means.
Maybe someone can explain what am I doing wrong? Is it with the .stream.map()? Because I never used it before. Or maybe the problem is somewhere else.
Method(List<contract> contractList){
List<FeeAccount> feeAccounts = new ArrayList<>();
feeAccounts = contractList
.stream()
.map(contract -> {
List<Fee> monthlyFees=...;
return monthlyFees.stream()
.map(monthlyFee -> {
FeeAccount account = new FeeAccount();
account.setFeeCode(monthlyFee.getFeeCode());
account.setDebtorAccount(contract.getDebtorAccount());
return account;
}).collect(Collectors.toList());
});}
You have two nested map operations. The outer transforms a contract to a List<FeeAccount>, and the inner transforms a Fee to a FeeAccount.
Hence, your pipeline results in a Stream<List<FeeAccount>> without a terminal operation.
If you add a .collect(Collectors.toList()) in the end, you'll get a List<List<FeeAccount>>.
If you want to merge all those inner lists into a single output list, you should use flatMap.
To obtain a flat List:
List<FeeAccount> feeAccounts =
contractList.stream()
.flatMap(contract -> {
List<Fee> monthlyFees=...;
return monthlyFees.stream()
.map(monthlyFee -> {
FeeAccount account = new FeeAccount();
account.setFeeCode(monthlyFee.getFeeCode());
account.setDebtorAccount(contract.getDebtorAccount());
return account;
});
})
.collect(Collectors.toList();
map() is an intermediate operation in a stream pipeline (please look at Stream operations and pipelines), which means that it returns a stream.
feeAccounts = contractList
.stream()
.map(...) // result of this operation is Stream<<List<FeeAccount>>
and not a List<FeeAccount>
You are missing a terminal operation like .collect(Collectors.toList() :
List<FeeAccount> feeAccounts = contractList
.stream()
.flatMap(monthlyFees -> monthlyFees.stream()
.map(monthlyFee -> {
FeeAccount account = new FeeAccount();
account.setFeeCode(monthlyFee.getFeeCode());
account.setDebtorAccount(contract.getDebtorAccount());
return account;
})
.collect(Collectors.toList());
flatMap transforms Stream<Stream<FeeAccount>> into just Stream<FeeAccount>

Java Optional orElseThrow with empty collection

I'm implementing a stream in which I use a collection listOfFoo to get ids of all items in that list and use them to get values of Bar instances.
I would like to ensure that this method will throw ResourceNotFoundException in case there is no items on bars list, although in the current state it checks if list bars is null and it is not, since it contains an empty list.
Could you please help me and suggest some solution?
List<Bar> bars = Optional.ofNullable(
listOfFoos.stream()
.map(Foo::getId)
.map(fooId -> service.getBars(fooId))
.filter(Objects::nonNull)
.collect(Collectors.toList()))
.orElseThrow(() -> new ResourceNotFoundException(Bar.class, OBJECT_NULL));
I don't really see the benefit of using Optional, it would be more readable without it :
List<Bar> bars = listOfFoos.stream()
.map(Foo::getId)
.map(service::getBars)
.collect(Collectors.toList());
if (bars.isEmpty()) {
throw new ResourceNotFoundException(Bar.class, OBJECT_NULL);
}
The book Effective Java mentions the following:
Container types, including collections, maps, streams, arrays, and
optionals should not be wrapped in optionals. (P.252)
Just add an Optional.filter for it then. You could do it as :
List<Bar> bars = Optional.ofNullable(
listOfFoos.stream().map(fooId -> service.getBars(fooId))
.filter(Objects::nonNull).collect(Collectors.toList()))
.filter(a -> !a.isEmpty())
.orElseThrow(() -> new ResourceNotFoundException(Bar.class, OBJECT_NULL));
Aside: By the implementation shared in the code, the list returned by the stream could not be null, so Optional.ofNullable could possibly be replaced by Optional.of.

Categories

Resources