Reputation: 37
I have a method like this where I'm using recursion with normal loop:
// example for the args: "START" "RUN" "RUN" "END" "RUN" "START" "RUN" "END" "RUN"
// expect: Listof.("START" "RUN" "RUN" "END", "START" "RUN" "END")
static List<String[]> findValidCommands(String[] args, List<String[]> listCommands) {
for (int i = 0; i < args.length; i++) {
if (args[i].matches("START")) {
for (int j = i + 1; j < args.length; j++) {
if ("END".equals(args[j])) {
listCommands.add(Arrays.copyOfRange(args, i, ++j));
return findValidCommands(Arrays.copyOfRange(args, j, args.length), listCommands);
}
}
}
}
return listCommands;
}
I'm a beginner. Please help me to improve my code with Stream
.
Upvotes: 1
Views: 165
Reputation: 21239
As others have mentioned, this problem is probably better solved using a non-stream approach.
That said, one stream-based approach in a newer version of Java would be to use a custom Gatherer
, using the Stream Gatherers feature in the upcoming Java 24. This gatherer can be used to gather elements into the desired sub-arrays, resulting in a Stream<String[]>
:
String[] args =
{"START", "RUN", "RUN", "END", "RUN", "START", "RUN", "END", "RUN"};
List<String[]> listCommands = Arrays.stream(args)
.gather(Gatherer.<String, List<String>, String[]>ofSequential(
ArrayList::new,
Gatherer.Integrator.ofGreedy(((state, element, downstream) -> {
if (!state.isEmpty() || element.equals("START")) {
state.add(element);
}
if (!state.isEmpty() && element.equals("END")) {
String[] group = state.toArray(String[]::new);
state.clear();
return downstream.push(group);
}
return true;
}))))
.toList();
// Demonstration that this works. Output: [START, RUN, RUN, END], [START, RUN, END]
System.out.println(listCommands.stream().map(Arrays::toString)
.collect(Collectors.joining(", ")));
This gatherer converts the Stream<String>
of commands into a Stream<String[]>
of command groups with a "START"
and "END"
pair.
The sequential gatherer uses a List<String>
as its state, skipping over elements until a "START"
element is found. Once a "START"
element is found, the elements are added to the list until an "END"
element is found. At this point, the list is converted to an array, pushed downstream, and then cleared. The preceding steps are once again taken by the gatherer, skipping elements until the next "START"
, and so on.
Upvotes: 0
Reputation: 159135
The code cannot be improved by Stream logic, quite the opposite.
It can however be improved by not using nested loops and not using recursion.
static List<String[]> findValidCommands(String... args) {
List<String[]> listCommands = new ArrayList<>();
for (int start = -1, i = 0; i < args.length; i++) {
if (start == -1 && args[i].equals("START")) {
start = i;
} else if (start != -1 && args[i].equals("END")) {
listCommands.add(Arrays.copyOfRange(args, start, i + 1));
start = -1;
}
}
return listCommands;
}
Test
List<String[]> listCommands = findValidCommands(
"START", "RUN", "RUN", "END", "RUN", "START", "RUN", "END", "RUN");
System.out.println(listCommands.stream().map(Arrays::toString).collect(Collectors.joining(", ")));
Output
[START, RUN, RUN, END], [START, RUN, END]
Upvotes: 2