I decided to take the functional approach in generating a string or random characters, so far I came up with this, it should perform better than boxing and then using a StringJoiner
as collector:
Random random = new Random();
String randomString = IntStream.concat(
random.ints(8, 'a', 'z'),
random.ints(8, 'A', 'Z')
)
.collect(
StringBuilder::new,
(sb, i) -> sb.append((char)i),
(sb1, sb2) -> sb1.append(sb2)
).toString();
I want to generate a stream of 16 characters, ranging from a-z or A-Z, the problem I have is that I do not know how to shuffle both streams.
I know that I am using IntStream.concat
here, which will simply concatenate both streams, I'm looking for either of the following:
IntStream.concat
that does the shuffling when merging the streams.sorted()
.Both ways are viable in my opinion, however I am especially intrigued by how to make an operator alike sorted()
. The key point here is that it is an operator that is stateful as it needs to see the whole stream before it can operate, is there a way to inject a stateful operator in a stream sequence?
So far the operations, excluding the needed work to shuffle them, seem to not be appropiate for a functional approach in Java 8.
You are thinking too twisted
Random random = new Random();
String randomString=random.ints(16, 0, 26*2).map(i->(i>=26? 'a'-26: 'A')+i)
.collect(StringBuilder::new,
StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
Since you already have a source of random values there is no point in calling for a shuffle function (which would not work very well with streams).
Note that you also could define the allowed chars in a String
explicitly and select them using:
random.ints(16, 0, allowed.length()).map(allowed::charAt)
Similar pattern applies to selecting from a random access List
.
Update: If you want to have code clearly showing the two ranges nature of the allowed characters you can combine your Stream.concat
approach with the char
selection solution described above:
StringBuilder allowed=
IntStream.concat(IntStream.rangeClosed('a', 'z'), IntStream.rangeClosed('A', 'Z'))
.collect(StringBuilder::new,
StringBuilder::appendCodePoint, StringBuilder::append);
String randomString=random.ints(16, 0, allowed.length()).map(allowed::charAt)
.collect(StringBuilder::new,
StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
(Note: I replaced range
with rangeClosed
which I suspect to match your original intentions while it does not do what Random.ints(…, 'a', 'z')
would do).
This is probably not as elegant as you hoped for but it works:
final Random random = new Random();
String randomString = IntStream.concat(random.ints(8, 'a', 'z'+1), random.ints(8, 'A', 'Z'+1))
.collect(StringBuilder::new, (sb, i) -> {
int l = sb.length();
if (l == 0) {
sb.append((char) i);
} else {
int j = random.nextInt(l);
char c = sb.charAt(j);
sb.setCharAt(j, (char) i);
sb.append(c);
}
}, (sb1, sb2) -> sb1.append(sb2)).toString();
System.out.println(randomString);
Alternatively you could do this:
final String randomString = random.ints(100, 'A', 'z' + 1)
.filter(i -> i <= 'Z' || i >= 'a').limit(16)
.collect(StringBuilder::new, (sb, i) -> sb.append((char) i),
StringBuilder::append).toString();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With