Reputation: 226
I have a class
public class Step
{
boolean isActive;
String name;
}
I have a Collection of type Steps. Without using streams this is what I have currently
StringBuilder stringBuilder = new StringBuilder();
for (Step step : steps)
{
List<String> nextStepNames = getNextStepNames(step);
List<String> conditions = getConditions(step);
for (int i = 0; i < nextStepNames.size(); i++)
{
stringBuilder.append("If ").append(step.getName()).append("is active, and condition (").append(conditions.get(i)).append(") is true, then move to ").append(nextStepNames.get(i)).append("\n");
}
}
If my step collection contains stepA, StepB and stepC, then this is my output:
If stepA is active, and condition (c1A) is true, then move to step1A
If stepA is active, and condition (c2A) is true, then move to step2A
If stepA is active, and condition (c3A) is true, then move to step3A
If stepB is active, and condition (c1B) is true, then move to step1B
If stepB is active, and condition (c2B) is true, then move to step2B
If stepB is active, and condition (c3B) is true, then move to step3B
If stepC is active, and condition (c1C) is true, then move to step1C
If stepC is active, and condition (c2C) is true, then move to step2C
If stepC is active, and condition (c3C) is true, then move to step3C
The nextStepNames
and conditions
list are the same size and the indexes in the lists correspond to each other.
I haven't been able to convert this code into streams. I not sure if its possible.
Upvotes: 1
Views: 2696
Reputation: 2203
Java lacks the abilities to solve the problem efficiently in an elegant pure functional style.
However, you could try something like
str = steps.stream()
.map(step ->
IntStream
.range(0, getNextStepNames(step).size())
.mapToObj(i -> Stream.of(
"If ",
step.getName(),
" is active, and condition (",
getConditions(step).get(i),
") is true, then move to ",
getNextStepNames(step).get(i),
"\n"))
.flatMap(Function.identity())
)
.flatMap(Function.identity())
.collect(Collectors.joining());
This quite inefficient due to the repeated evaluation of getNextStepNames
and getConditions
and the inability to allocate the complete output buffer in advance.
Of course you could try to mitigate this by using third party libraries, but imho it's not worth the effort.
Your solution is more efficient and much easier to understand and maintain. You cold further improve this by initializing the StringBuilder
with a size that is equal or a little bit greater than the final output size.
Upvotes: 1
Reputation: 23057
Try this:
String output = Arrays.stream(steps) // if it's an array or steps.stream() for a list
.flatMap(step -> IntStream.range(0, getNextStepNames(step).size())
.mapToObj(i -> String.format(
"If %s is active, and condition (%s) is true, then move to %s",
step.getName(),
getConditions(step).get(i),
getNextStepNames(step).get(i))))
.collect(Collectors.joining("\n"));
Our initial stream only contains three elements (step A, B and C), so for each element, we need another stream. We create an IntStream with all valid indexed of both lists. We're mapping them to a string, getting the elements from the two methods. I used String.format
, but this of course can be replaced by a StringBuilder
or simple string concatenation.
At this point, we have streams within streams. We need to flattenize it to a single stream, simply by calling flatMap
.
At last, we can join all elements using \n
as glue.
It's a shame that getNextStepNames
and getConditions
both seem related, but yet are separate. But that's another story.
Upvotes: 0
Reputation: 32046
One step closer to that could be :
for (Step step : steps) {
List<String> nextStepNames = getNextStepNames(step);
List<String> conditions = getConditions(step);
IntStream.range(0, nextStepNames.size())
.forEach(i -> stringBuilder.append("If ")
.append(step.getName())
.append("is active, and condition (")
.append(conditions.get(i))
.append(") is true, then move to ")
.append(nextStepNames.get(i)).append("\n"));
}
Upvotes: 0