Here is an alternative implementation.
IntStream
of code points
I recommend making a habit of using code points integer numbers rather than the legacy type char
when working with individual characters. As a 16-bit value, a char
is physically incapable of representing most characters.
We can generate the range of code points from a
to z
(97 to 122) from an IntStream
. The Character.toString( codePoint )
method generates a single-character String
object from our code point integer.
List < String > characters =
IntStream
.rangeClosed( "a".codePointAt( 0 ) , "z".codePointAt( 0 ) ) // ( 97 inclusive, 122 inclusive )
.mapToObj( Character :: toString )
.toList();
characters.toString() = [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]
Collect each String
of three combined characters into List
.
List < String > combinations = new ArrayList <>( characters.size() ^ 3 );
Then use the for-each syntax to loop that source list three times, nested, once for each position of your desired outputs.
for ( String firstCharacter : characters )
{
for ( String secondCharacter : characters )
{
for ( String thirdCharacter : characters )
{
combinations.add( firstCharacter + secondCharacter + thirdCharacter );
}
}
}
Calling List#size
gives you count you desire. Though mathematically we know that count should be ( 26 ^ 3 ) = 17,576.
System.out.println( combinations.size() + " combinations = " + combinations );
When run.
17576 combinations = [aaa, aab, aac, aad, aae, aaf, … zzw, zzx, zzy, zzz]
One-liner by using Stream#flatMap
We can even reduce that code down to a single-line, using the impressive code from the Comment by Holger.
The key part is a call to Stream#flatMap
, used to generate many values from one value. To quote the Javadoc:
The flatMap() operation has the effect of applying a one-to-many transformation to the elements of the stream, and then flattening the resulting elements into a new stream.
By the way, concat
is a static
method. This seems a bit odd, given the other fluent-style methods on streams. If curious, see this Question.
So we start by transforming our stream of code points into a stream of Strings
, each containing a single character. For each of our first characters, we create a bunch more code points transformed into String
objects, for our second characters. We call flatMap
again for each of those second characters, each one generating yet another stream of code points transformed into String
objects for our third position. From there the first, second, and third characters are combined into a resulting string which we collect into our end result, an unmodifiable List< String >
.
We get the same 17,576 combinations seen above.
List < String > combinations =
IntStream
.rangeClosed( "a".codePointAt( 0 ) , "z".codePointAt( 0 ) )
.mapToObj( Character :: toString )
.flatMap(
first ->
IntStream
.rangeClosed( "a".codePointAt( 0 ) , "z".codePointAt( 0 ) )
.mapToObj( Character :: toString )
.flatMap( second ->
IntStream
.rangeClosed( "a".codePointAt( 0 ) , "z".codePointAt( 0 ) )
.mapToObj( third -> first + second + Character.toString( third ) )
)
)
.toList();
Multiple sets of inputs
The code above assumes we have a single range of characters to mix and match. Be aware that we could combine multiple ranges. Just call Stream.concat
while passing a pair of streams.
In this example we mix and match lowercase ab
along with uppercase AB
. For a total of four characters being used in three position, we expect 4 ^ 3 = 64 combinations.
List < String > combinations =
IntStream
.concat(
IntStream.rangeClosed( "a".codePointAt( 0 ) , "b".codePointAt( 0 ) ) ,
IntStream.rangeClosed( "A".codePointAt( 0 ) , "B".codePointAt( 0 ) )
)
.mapToObj( Character :: toString )
.flatMap(
first ->
IntStream
.concat(
IntStream.rangeClosed( "a".codePointAt( 0 ) , "b".codePointAt( 0 ) ) ,
IntStream.rangeClosed( "A".codePointAt( 0 ) , "B".codePointAt( 0 ) )
)
.mapToObj( Character :: toString )
.flatMap( second ->
IntStream
.concat(
IntStream.rangeClosed( "a".codePointAt( 0 ) , "b".codePointAt( 0 ) ) ,
IntStream.rangeClosed( "A".codePointAt( 0 ) , "B".codePointAt( 0 ) )
)
.mapToObj( third -> first + second + Character.toString( third ) )
)
)
.toList();
64 combinations.toString() = [aaa, aab, aaA, aaB, aba, abb, abA, abB, aAa, aAb, aAA, aAB, aBa, aBb, aBA, aBB, baa, bab, baA, baB, bba, bbb, bbA, bbB, bAa, bAb, bAA, bAB, bBa, bBb, bBA, bBB, Aaa, Aab, AaA, AaB, Aba, Abb, AbA, AbB, AAa, AAb, AAA, AAB, ABa, ABb, ABA, ABB, Baa, Bab, BaA, BaB, Bba, Bbb, BbA, BbB, BAa, BAb, BAA, BAB, BBa, BBb, BBA, BBB]