for (Issue issue : issues) {
if (issue.getSubtasks().spliterator().estimateSize() > 0) {
alreadyCreated = false;
for (Subtask subtask : issue.getSubtasks()) {
if (subtask.getSummary().contains("sometext")) {
alreadyCreated = true;
break;
}
}
if (!alreadyCreated) {
ids.add(issue.getKey());
}
} else {
ids.add(issue.getKey());
}
}
I'm not so expert with Java stream API, but I'm pretty sure there's some way to simplify the code above with using lambda expressions. Would be a great help to understand it even better!
Some notes:
Issues and getSubtasks() are returning back Iterable<> types.
You can use filter to remove unwanted elements and map to get the key, then collect to a List.
StreamSupport.stream(issues.spliterator(), false).filter(x ->
StreamSupport.stream(x.getSubtasks().spliterator(), false)
.noneMatch(y -> y.getSummary().contains("sometext"))
).map(Issue::getKey).collect(Collectors.toList());
I can't be certain what is streamable in your example so I'm going to provide an alternate solution that doesn't require streams but is at least, if not more, efficient. It just uses a different technique but essentially the same logic.
if size <= 0, then the for loop is skipped and the key is added.
if size > 0 then then for loop is excecuted. Then if any of the summaries contains the text, the outer loop proceeds normally, otherwise, the loop falls thru ant the key is added.
outer:
for (Issue issue : issues) {
if (issue.getSubtasks().spliterator().estimateSize() > 0) {
for (Subtask subtask : issue.getSubtasks()) {
if (subtask.getSummary().contains("sometext")) {
continue outer;
}
}
}
ids.add(issue.getKey());
}
}
You're adding the issue key to ids if there exists no subtask with a summary containing "sometext."
issues.stream()
.filter(i -> i.getSubtasks().stream()
.noneMatch(s -> s.getSummary().contains("sometext")))
.map(Issue::getKey)
.forEach(ids::add);
I think the subtasks size check is at least superfluous and possibly dangerous, if it's possible for it to estimate the size as zero when subtasks exist. But if getSubtasks() returns some non-collection that can't easily be streamed and this estimateSize() call is necessary then that just changes things a little bit:
issues.stream()
.filter(i -> {
Spliterator<Subtask> split = i.getSubtasks().spliterator();
return split.estimateSize() == 0 ||
StreamSupport.stream(split, false)
.noneMatch(s -> s.getSummary().contains("sometext"));
})
.map(Issue::getKey)
.forEach(ids::add);
Related
I have a list of documents that I need to save on mongoDB, after having executing some process on them.
I have a reactive mongoDB dependency so I would like to use it. I would like to avoid the usage of another dependency (the non reactive one) if possible.
The 2nd document process should begin after the 1st document has been saved.
Mono<List<Index>> deferredCreate = Mono.defer(() -> index
.flatMapMany(Flux::fromIterable)
.flatMapSequential(entity -> {
repository.process(entity).subscribe();
return entity;
})
)
.collectList());
with
public Mono<IndexDocument> process(Index index) {
if(someCondition) { return mongoOperations.save(index); }
else { return mongoOperations.findAndReplace(query, index); }
}
Here, the list is process index by index but index n+1 process begins after index n begins and I need it begins after n process is finished.
I cannot put .block() instead of subscriber otherwise I have an error (block cannot be used in parallel stream). I tried with concatMap but this is the same.
Is there any way to do it ?
I use reactive programming because this is part of a larger process that needs to be reactive.
As I mentioned in the comment, you should not subscribe explicitly. flatMapSequential will subscribe behind the scene and preserve the original order
Mono<List<Index>> deferredCreate = Mono.defer(() -> index
.flatMapMany(Flux::fromIterable)
.flatMapSequential(entity ->
repository.process(entity)
.thenReturn(entity)
)
.collectList());
Here is my Java code:
static Map<BigInteger, Integer> cache = new ConcurrentHashMap<>();
static Integer minFinder(BigInteger num) {
if (num.equals(BigInteger.ONE)) {
return 0;
}
if (num.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) {
//focus on stuff thats happening inside this block, since with given inputs it won't reach last return
return 1 + cache.computeIfAbsent(num.divide(BigInteger.valueOf(2)),
n -> minFinder(n));
}
return 1 + Math.min(cache.computeIfAbsent(num.subtract(BigInteger.ONE), n -> minFinder(n)),
cache.computeIfAbsent(num.add(BigInteger.ONE), n -> minFinder(n)));
}
I tried to memoize a function that returns a minimum number of actions such as division by 2, subtract by one or add one.
The problem I'm facing is when I call it with smaller inputs such as:
minFinder(new BigInteger("32"))
it works, but with bigger values like:
minFinder(new BigInteger("64"))
It throws a Recursive Update exception.
Is there any way to increase recursion size to prevent this exception or any other way to solve this?
From the API docs of Map.computeIfAbsent():
The mapping function should not modify this map during computation.
The API docs of ConcurrentHashMap.computeIfAbsent() make that stronger:
The mapping function must not modify this map during computation.
(Emphasis added)
You are violating that by using your minFinder() method as the mapping function. That it seems nevertheless to work for certain inputs is irrelevant. You need to find a different way to achieve what you're after.
Is there any way to increase recursion size to prevent this exception or any other way to solve this?
You could avoid computeIfAbsent() and instead do the same thing the old-school way:
BigInteger halfNum = num.divide(BigInteger.valueOf(2));
BigInteger cachedValue = cache.get(halfNum);
if (cachedValue == null) {
cachedValue = minFinder(halfNum);
cache.put(halfNum, cachedValue);
}
return 1 + cachedValue;
But that's not going to be sufficient if the computation loops. You could perhaps detect that by putting a sentinel value into the map before you recurse, so that you can recognize loops.
I have the following code:
boolean found = false;
for (Commit commit : commits.values()) {
if (commit.getLogMessage().compareTo(commitMessageToSearch) == 0) {
System.out.println(commit.getSha());
found = true;
}
}
if (!found) {
System.out.println("aksdhlkasj");
}
Is there some way to write this succinctly using streams or anything else in Java
You can use Stream#filter along with Stream#findFirst.
System.out.println(commits.values().stream()
.filter(commit -> commit.getLogMessage().compareTo(commitMessageToSearch) == 0)
.findFirst().map(Commit::getSha).orElse("aksdhlkasj"));
In case you want to print out all the occurrences and print some String only in case, there was no item found, I am afraid there is no way other than collecting all the relevant sha values into a list and checking for its emptiness using Optional:
commits.values()
.stream()
.filter(commit -> commit.getLogMessage().compareTo(commitMessageToSearch) == 0)
.map(Commit::getSha)
.peek(System.out::println)
.collect(Collectors.collectingAndThen(Collectors.toList(), Optional::of))
.filter(List::isEmpty)
.ifPresent(emptyList -> System.out.println("aksdhlkasj"));
Although the intermediate output Optional<List<?>> is against common sense, it helps the further processing using Optinal and comfortably handling the case the list is empty.
However, this form is in my opinion more readable:
List<String> shaList = commits.values()
.stream()
.filter(commit -> commit.getLogMessage().compareTo(commitMessageToSearch) == 0)
.map(Commit::getSha)
.peek(System.out::println)
.collect(Collectors.toList());
if (shaList.isEmpty()) {
System.out.println("aksdhlkasj");
}
The following options are available:
Use StreamEx library (maven repo) and its quasi-intermediate operation StreamEx::ifEmpty:
import one.util.streamex.StreamEx;
// ...
String msgSearch = "commit message";
StreamEx.of(commits.values())
.filter(c -> c.getLogMessage().compareTo(msgSearch) == 0)
.ifEmpty("no commit message found: " + msgSearch)
.map(Commit::getSha)
.forEach(System.out::println);
Plus: very concise and clean, single run
Minus: use of a 3rd-party lib
Check the contents of the stream using short-circuiting match without redundant collecting into a list just to check if it's empty
if (commits.values().stream()
.map(Commit::getLogMessage) // equals should be equivalent to compareTo == 0
.anyMatch(msgSearch::equals)
) {
commits.values().stream()
.filter(c -> c.getLogMessage().compareTo(msgSearch) == 0)
.map(Commit::getSha)
.forEach(System.out::println);
} else {
System.out.println("no commit message found: " + msgSearch);
}
Plus: using standard API (no 3-rd party) without side-effect
Minuses: too verbose, in the worst case (long list with the matching element in the end) double iteration of the stream
Use effectively final variable and its setting from the stream as a side effect.
Disclaimer: usage of stateful streams and side effects are not recommended in general:
The best approach is to avoid stateful behavioral parameters to stream operations entirely...
Side-effects in behavioral parameters to stream operations are, in general, discouraged, as they can often lead to unwitting violations of the statelessness requirement, as well as other thread-safety hazards.
A small number of stream operations, such as forEach() and peek(), can operate only via side-effects; these should be used with care
Thus, if AtomicBoolean is carefully selected as a thread-safe and fast container of a boolean value for found flag, which is only set to true from inside the stream (never reset), the following solution may be offered and the risks of safety and performance are mitigated:
// effectively final and thread-safe container boolean
AtomicBoolean found = new AtomicBoolean();
commits.values().stream()
.filter(c -> c.getLogMessage().compareTo(commitMessageToSearch) == 0)
.map(Commit::getSha)
.forEach(sha -> {
found.set(true);
System.out.println(sha);
});
if (!found.get()) {
System.out.println("none found");
}
Plus: using standard API (no 3rd-party library), single run, no redundant collection
Minus: using side-effect, discouraged by purists, just mimics of for-each loop
I have a code snippet like this
if (CollectionUtils.isNotEmpty(target.getSpecifications())) {
for (final SpecificationData data : target.getSpecifications()) {
if (StringUtils.isNotEmpty(data.getModelName())) {
productLinks.add(DETAILS);
break;
} else if (StringUtils.isNotEmpty(data.getModelNumber())) {
productLinks.add(DETAILS);
break;
} else if (StringUtils.isNotEmpty(data.getMaterial())) {
productLinks.add(DETAILS);
break;
} else if (StringUtils.isNotEmpty(data.getColour())) {
productLinks.add(DETAILS);
break;
}
}
}
As you can see, I am iterating a collection and doing a check in order to populate the link "details" in front end. The idea is that I need to populate this link at least one of the attribute length inside current object should be > 0. Because of the fact, I have used so many break statement, this snippet is failing in sonar build process
What do I need? I request you guys to share me the simplest version of the above code or refactored code using latest JDK and yes we are using JDK 11 and I am not pretty sure about the methods that I need to use for this kind of check.
If there is no other alternatives how to overcome this "Loops should not contain more than a single "break" or "continue" statement" sonar issue.
Appreciate your time and effort on this.
The easiest solution could be just to join multiple if statements into one though it may not be helpful against Sonar rules :)
if (CollectionUtils.isNotEmpty(target.getSpecifications())) {
for (final SpecificationData data : target.getSpecifications()) {
if (StringUtils.isNotEmpty(data.getModelName())
|| StringUtils.isNotEmpty(data.getModelNumber())
|| StringUtils.isNotEmpty(data.getMaterial())
|| StringUtils.isNotEmpty(data.getColour())
) {
productLinks.add(DETAILS);
break;
}
}
}
However, you may use stream operations such as filter and findFirst like this without any for loop and break statements:
if (CollectionUtils.isNotEmpty(target.getSpecifications())) {
target.getSpecifications().stream()
.filter(x ->
Stream.of(x.getModelName(), x.getModelNumber(), x.getMaterial(), x.getColour())
.filter(StringUtils::isNotEmpty)
.findFirst()
.isPresent()
)
.findFirst()
.ifPresent(x -> productLinks.add(DETAILS));
}
UPDATE
For this specific case it is also possible to use flatMap to detect any first non-empty property and perform an action:
if (CollectionUtils.isNotEmpty(target.getSpecifications())) {
target.getSpecifications().stream()
.flatMap(x -> Stream.of(x.getModelName(), x.getModelNumber(), x.getMaterial(), x.getColour()))
.filter(StringUtils::isNotEmpty)
.findFirst()
.ifPresent(x -> productLinks.add(DETAILS));
}
Consider the case where an if condition needs to evaluate an array or a List. A simple example: check if all elements are true. But I'm looking for generic way to do it
Normally I'd do it like that:
boolean allTrue = true;
for (Boolean bool : bools){
if (!bool) {
allTrue = false;
break;
}
}
if (allTrue){
// do Something
}
But now I'd like to hide it into my if condition. I tried using Lambda Expressions for this, but it's not working:
if (() -> {
for (Boolean bool : bools)
if (!bool)
return false;
return true;
}){
// do something
}
If this were working I could do something more complicated like
if (() -> {
int number = 0;
for (MyObject myobject : myobjects)
if (myObject.getNumber() != 0)
numbers++;
if (numbers > 2)
return false;
return true;
}{
//do something
}
Is there a better way to do it is it just a syntax error?
UPDATE
I'm not talking about the boolean array, rather looking for a generic way to achieve that.
You can write, given for instance a List<Boolean>:
if (!list.stream().allMatch(x -> x)) {
// not every member is true
}
Or:
if (list.stream().anyMatch(x -> !x)) {
// at least one member is false
}
If you have an array of booleans, then use Arrays.stream() to obtain a stream out of it instead.
More generally, for a Stream providing elements of (generic) type X, you have to provide a Predicate<? super X> to .{all,any}Match() (either a "full" predicate, or a lambda, or a method reference -- many things go). The return value of these methods are self explanatory -- I think.
Now, to count elements which obey a certain predicate, you have .count(), which you can combine with .filter() -- which also takes (whatever is) a Predicate as an argument. For instance checking if you have more than 2 elements in a List<String> whose length is greater than 5 you'd do:
if (list.stream().filter(s -> s.length() > 5).count() > 2L) {
// Yup...
}
Your problem
Your current problem is that you use directly a lambda expression. Lambdas are instances of functional interfaces. Your lambda does not have the boolean type, that's why your if does not accept it.
This special case's solution
You can use a stream from your collections of booleans here.
if (bools.stream().allMatch((Boolean b)->b)) {
// do something
}
It is actually much more powerful than this, but this does the trick I believe.
General hint
Basically, since you want an if condition, you want a boolean result.
Since your result depends on a collection, you can use Java 8 streams on collections.
Java 8 streams allow you to do many operations on a collection, and finish with a terminal operation. You can do whatever complicated stuff you want with Stream's non-terminal operations. In the end you need one of 2 things:
use a terminal operation that returns a boolean (such as allMatch, anyMatch...), and you're done
use any terminal operation, but use it in a boolean expression, such as myStream.filter(...).limit(...).count() > 2
You should have a look at your possibilities in this Stream documentation or this one.