The presented way is not a functional solution. It uses streams but it relies on forEach
, which is the imperative way of using a Stream.
To make it better, we need to think about what we really want to do. We want to build all the permutations of the characters in a String. That means we need to have a method permutations(String)
that will return all the permutations. Using Java 8, we can return a Stream<String>
which will corresponds to the Stream of all the permutations.
To build those permutations, we can have a recursive algorithm:
- If the String is empty, there are no characters, so the only result is a Stream that contains the empty String. This is the base case.
- If the String is non-empty:
- we remove the first character, build all the permutations without it and then prepend the first character to all the results.
- then we remove the second character, build all the permutations without it and then prepend the second character to all the results.
- etc. until we hit the last character.
Using Java 8, this is an implementation:
public static Stream<String> permutations(String str) {
if (str.isEmpty()) {
return Stream.of("");
}
return IntStream.range(0, str.length())
.boxed()
.flatMap(i ->
permutations(str.substring(0, i) + str.substring(i+1)).map(t -> str.charAt(i) + t)
);
}
The base-case is handled by the if
: when the String is empty, we return Stream.of("")
: a Stream that contains only the empty String/
The main part is after: we create an IntStream
over the indexes of the String. (Since there are unfortunately no flatMapToObj
operation, we need to box each int
into an Integer
.) Then that's where the magic happens: for each index, we recurse by building all the permutations again, leaving the index i
, and each of the result is mapped to prepend the character at index i
. Since this recursive call returns a Stream<String>
, this result is flat mapped (with flatMap
) to have a single Stream.
With this utility method, your code is then just
permutations("ferrao").forEach(System.out::println);
That brings also a big advantage: the method is returning a Stream<String>
, that is to say, the method does not actually perform the calculation. It is then up-to the caller to decide when to do it, if they want it to happen in parallel or not, and especially what the end container will be (a List
, a Set
, no container at all with just post-processing afterwards).
For example, to have it performed in parallel, you just need to do
permutations("ferrao").parallel().forEach(System.out::println);