Streams - anyMatch in filter produces IllegalStateException - java

I am trying to iterate through two lists and I am getting an IllegalStateException: stream has already been operated upon or closed
file().forEach(line -> {
boolean match = ordersSupplier.get().anyMatch(order -> order == line.id);
if (match) {
//do something;
}
});
I know that anyMatch is a terminal operation, thats why I am getting this error. Can I parameterize a Supplier für anyMatch with the id of the line? Any Idea?

Collect the order ids in a Set and filter your line with the Stream API. Then you can execute your code in a forEach.
Set<String> orderIds = ordersSupplier.get().collect(toSet());
file().stream()
.filter(l -> orderIds.contains(l.id))
.forEach(
line -> {
//do something;
});

Related

Perform a filter within a function call

Is there a way I could perform a filter within the steps of the following function call?
The following works fine but I wish to add a null/empty check on getResults which returns a list of Results object.
I could try to .stream() on it like getResults().stream().filter();
But kind of pointless since getResults().stream() could potentially throw a null pointer already.
Also this returns a stream of Result. But I want to do a check on the List of Result itself, like:
.filter(CollectionUtils::isNotEmpty)
Is there a way to do this?
public MyFunc(Helper helper) {
Function<String, HttpEntity<Request>> createRequestFunction = helper::createRequest;
this.fetch = createRequestFunction
.andThen(helper::getResponse)
.andThen(Response::getQuery)
.andThen(QueryResult::getResults)
// I want to make a filter here .filter(CollectionUtils::isNotEmpty) to ensure
// getResults (It is a List<Result> type) is not null or empty
.andThen(results -> results.stream()
.map(Result::getKey)
.filter(StringUtils::isNotBlank)
.map(s -> s.split("\\|"))
.filter(data -> data.length == 2)
.map(data -> data[1])
.collect(Collectors.toList()));
}
createRequestFunction has type of Function<String, HttpEntity<Request>>
Function itself doesn't have a filter operation. It is possible to either append additional function via andThen or prepend via compose.
What is expected behaviour if QueryResult::getResults is null? What is the value of this.fetch should be?
One possible option is to wrap result of QueryResult::getResults into an Optional. Something similar to this:
this.fetch = createRequestFunction
.andThen(helper::getResponse)
.andThen(Response::getQuery)
.andThen(QueryResult::getResults)
.andThen(Optinal::ofNullable);
so the result of this.fetch is Optinal<List<Result>> and it is possible to execut different logic based on the fact if optional is empty or not.
e.g
return
this.fetch // Optinal<List<Result>>
.getOrElse(Collections.emptyList()) // value of optional if it is not empty, or empty list otherwise
.stream()
.map(Result::getKey)
.filter(StringUtils::isNotBlank)
.map(s -> s.split("\\|"))
.filter(data -> data.length == 2)
.map(data -> data[1])
.collect(Collectors.toList())

JAVA-STREAM : re-use double a DoubleStream

I need a trick to solve this problem i'm using Java 1.8 And I retrieved a an object from a method that returns a DoubleStream object. So, the problem is I could not reuse the stream after it has been consumed.
Here is the first version of the code :
DoubleStream stream = object.getvalueStream(a,b);
if(condtion)
stream.map(v -> v * 2);
stream.forEach(value -> {
// do something
}
The problem is that once the condition is true, the stream is consumed And I can not reuse it. So I tried to use a supplier to return a doubleStream from supplier and iterate overt it.
But still the same problem as I try to recover the stream from my stream object which is already used.
Here is my updated code :
DoubleStream stream = object.getvalueStream(a,b);
if(condtion)
stream.map(v -> v * 2);
Supplier>DoubleStream> streamSupplier = () -> DoubleStream.of(stream.toArray());
streamSupplier.get().forEach(value -> {
//Do something
But I still have the same problem since I create my supplier from my stream already used if the condition is true.
Thanks for your help.
once the condition is true, the stream is consumed And I can not reuse it
Intermediate operations (e.g. map) return a new stream, so you need to reassign the stream after the intermediate operation (map).
I.e.
DoubleStream stream = object.getvalueStream(a,b);
if (condition) {
stream = stream.map(v -> v * 2);
}
stream.forEach(value -> {
// do something
}
Note terminal operations (e.g. foreach) do not return a stream. So if you want many terminal operations, you should collect the stream so it can be reused.
Note also, there is also an intermediate version of foreach called peek if you wish to chain foreach (peek) calls.
Streams in Java are not up to be reused. You should collect the result and stream twice like
List<Double> doubles = object.getvalueStream(a,b).boxed().collect(toList());
if(condition) {
doubles = doubles.stream().map(v -> v * 2).boxed().collect(toList);
}
// and further processing here
doubles.stream().forEach(v ->
...
);
You can use map() if it is important for you to keep the stream without collecting it. The only drawback in this approach is that you have to check the condition each time
DoubleStream stream = object.getvalueStream(a,b).map(v-> condition ? v*2 : v).forEach(...);
Or just assign the right Stream to the variable
DoubleStream stream = condition ? object.getvalueStream(a,b).map(v->v*2) : object.getvalueStream(a,b).map(v->v*2).forEach(...);

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>

How to handle nullable lists using java 8?

I'm making a service call and trying to handle response.
Response might have a list of something. That list might be null.
Moreover, if list not null or not empty, then
it needs to be filtered.
In the code "entry" reference might be null if filtering gives nothing or response list is empty or null.
Currently i'm getting NPE when i try to use stream() on a null response list.
How can i handle this situation?
#Getter
public class ServiceResponse {
List<ResponseEntry> entryList;
}
#Getter
public class ResponseEntry {
String value;
}
ServiceResponse serviceResponse = service.getServiceResponse();
ResponseEntry entry = serviceResponse.getEntryList()
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);
if (entry == null) { ... }
if list not null or not empty, then it needs to be filtered.
No need for Optional here, as it's not intended to replace simple if checks.
ResponseEntry entry = null;
List<ResponseEntry> responseEntries = serviceResponse.getEntryList();
if(responseEntries != null && !responseEntries.isEmpty()){
entry = responseEntries.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);
}
reads "if responseEntries is not null and responseEntries is not empty then apply the filter operation and find the first item or else null". Very readable.
On the other hand, the optional approach:
ResponseEntry entry = Optional.ofNullable(serviceResponse.getEntryList())
.orElseGet(() -> Collections.emptyList())
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst();
if(!entry.isPresent()){ ... } // or entry.ifPresent(e -> ...) depending on the logic you're performing inside the block
unnecessarily creates objects that could be avoided and not really the intention of optional to be used as a substitute for simple "if" checks.
Stream.ofNullable (Java-9)
Returns a sequential Stream containing a single element, if non-null,
otherwise returns an empty Stream.
Current Code
ResponseEntry entry = serviceResponse.getEntryList() // List<ResponseEntry>
.stream() // NPE here // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter
.findFirst() // Optional<ResponseEntry>
.orElse(null); // or else null
Updated Code
ResponseEntry entry = Stream.ofNullable(serviceResponse.getEntryList()) // Stream<List<ResponseEntry>>
.flatMap(List::stream) // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter here
.findFirst() // Optional<ResponseEntry>
.orElse(null); // or else null
Optional.stream (Java-9)
returns a sequential Stream containing only that value, otherwise
returns an empty Stream.
ResponseEntry entry = Optional.ofNullable(serviceResponse.getEntryList())
.stream() // Stream<List<ResponseEntry>>
.flatMap(List::stream) // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter here
.findFirst() // Optional<ResponseEntry>
.orElse(null); // or else null
Optional.isEmpty(Java-11)
If a value is not present, returns true, otherwise false
Optional<ResponseEntry> entry = Optional.ofNullable(serviceResponse.getEntryList()) // Optional<List<ResponseEntry>>
.orElseGet(Collections::emptyList) // or else empty List
.stream() // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter
.findFirst(); // Optional<ResponseEntry>
if (entry.isEmpty()) { // !entry.isPresent in java-8
// Do your work here
}
In Java 9, you could use the new method Objects.requireNonNullElse(T,T):
Objects.requireNonNullElse(serviceResponse.getEntryList(),
Collections.emptyList())
Apache Commons Collections actually has a method ListUtils.emptyIfNull(List<T>) which returns an empty list if the argument list is null. That's even better, but Objects.requireNonNullElse is the closest thing to it in Java SE.
If you're restricted to just Java 8, then I agree with Aomine's answer that trying to do something like go through Optional is worse than an if statement.
You could simply use the ternary operator:
ServiceResponse serviceResponse = service.getServiceResponse();
List<ResponseEntry> list = serviceResponse.getEntryList();
ResponseEntry entry = (list == null ? Collections.emptyList() : list)
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);
if (entry == null) { ... }
Sometimes, traditional is better IMO.
Another option would be to use the Optional monad:
Optional<ResponseEntry> entry = Optional.ofNullable(serviceResponse.getEntryList()).flatMap(list ->
list.stream().filter(e -> "expectedValue".equals(e.getValue())).findFirst()
);
if (!entry.isPresent()) {
…
}
You might even use orElseGet instead of that if statement if your objective is to build (and return) a value, instead of executing a side effect.
I am new to Optional and I may be wrong. Logic can be written like below if you want to have logic including only optional.
ServiceResponse serviceResponse = service.getServiceResponse();
ResponseEntry entry =
Optional.of(CollectionUtils.isNotEmpty(serviceResponse.getEntryList()))
.filter(BooleanUtils::isTrue)
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);

Zip reactive flow with itself

I'm using Java Reactor Core, and I have a reactive Flux of objects. For each object of the Flux I need to do an external query that will return one different object for each input. The newly generated Flux needs then to be zipped with the original one - so the items of the 2 Flux must be synchronized and generated in the same order.
I'm just re-using the same flow twice, like this:
Flux<MyObj> aTest = Flux.fromIterable(aListOfObj);
Flux<String> myObjLists = aTest.map(o -> MyRepository.findById(o.name)).map(o -> {
if (!o.isPresent()) {
System.out.println("Fallback to empty-object");
return "";
}
List<String> l = o.get();
if (l.size() > 1) {
System.out.println("that's bad");
}
return l.get(0);
});
Flux.zip(aTest, myObjLists, (a, b) -> doSomethingWith(a,b))
Is it the right way to do it? If the myObjLists emits an error, how do I prevent the zip phase to skip the failing iteration?
I've finally opted for using Tuples and Optionals (to prevent null-items that would break the flux), so that I don't need to re-use the initial Flux:
Flux<Tuple<MyObj, Optional<String>>> myObjLists = Flux.fromIterable(aListOfObj)
.map(o -> Tuples.of(o, Optional.ofNullable(MyRepository.findById(o.name))
.flatMap(t -> {
if (!t.getT2().isPresent()) {
System.out.println("Discarding this item");
return Flux.empty();
}
List<String> l = t.getT2().get();
if (l.size() > 1) {
System.out.println("that's bad");
}
return Tuples.of(t.getT1(), l.get(0));
})
.map(t -> doSomethingWith(t.getT1(),t.getT2()))
Note that the flatMap could be replaced with a .map().filter(), removing tuples with missing Optional items

Categories

Resources