How to shuffle a stream using the Stream API?

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();