3

I'm trying to create a function which returns a random string of alphabetic characters, but I'm trying to do it using lambdas, my function looks like:

public static String nextString() {
  return IntStream.range(0, 10).map(i -> getRandomChar()).XXX
}

private static Character getRandomChar() {
  //returns a random Character object
}

I don't know what to set on XXX.

I fixed it in a similar way by doing:

public static String nextString() {
    StringBuilder randomString = new StringBuilder();
    IntStream.range(0, stringLenght).forEach(i -> randomString.append(getRandomChar()));
    return randomString.toString();
}

But I'd prefer not to create a new StringBuilder each time the method is used and keep states and variables for a simple method.

2
  • stackoverflow.com/questions/27522563/… ? Commented Apr 4, 2017 at 18:06
  • If I try return IntStream.range(0, strLenght).map(i -> getRandomChar()).collect(Collectors.joining(""));got compilation problems, I tried that before, because of this error I published my question Commented Apr 4, 2017 at 18:29

2 Answers 2

4

Something like this:

public static String nextString() {
    return IntStream.range(0, 10).boxed().map(i -> getRandomChar(i)).collect(Collectors.joining());
}

private static String getRandomChar(int i) {
    return String.valueOf((char)i);
}
Sign up to request clarification or add additional context in comments.

2 Comments

I don't get why this code works only if getRandomChar returns a String but not when returns Character
Because Collectors.joining() expected Stream<String>. You can create own Collector like this: Collector<Character, StringBuilder, String> collector = Collector.of(StringBuilder::new, StringBuilder::append, (r1, r2) -> { r1.append(r2); return r1; }, StringBuilder::toString);
1

It’s not clear what getRandomChar() does that it is worth building another Stream operation around it. It’s much simpler to stream over random values in the first place.

E.g., the following code generates random strings consisting of upper case letters in the A-Z range:

public static String nextString() {
    return ThreadLocalRandom.current().ints(stringLength, 'A', 'Z'+1)
        .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
        .toString();
}

If the intended characters do not form a consecutive range, you can use a string constant naming the allowed characters, e.g.

public static String nextString() {
    String allowed = "XO+";
    return ThreadLocalRandom.current().ints(stringLength, 0, allowed.length())
        .map(allowed::charAt)
        .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
        .toString();
}

Granted, this collector doesn’t hide the StringBuilder the same way, Collectors.joining() does, but on the other hand, this operation has no boxing overhead and doesn’t create temporary strings.

You can use a similar operation to built the character pool String:

static final String ASCII_LETTERS_AND_NUMBERS =
        IntStream.concat(IntStream.concat(
            IntStream.rangeClosed('A', 'Z'), IntStream.rangeClosed('a', 'z')),
            IntStream.rangeClosed('0', '9'))
        .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
        .toString();
public static String nextString() {
    return ThreadLocalRandom.current()
        .ints(stringLength, 0, ASCII_LETTERS_AND_NUMBERS.length())
        .map(ASCII_LETTERS_AND_NUMBERS::charAt)
        .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
        .toString();
}

or, to challenge the reader of the random string:

static final String ALL_LETTERS_AND_NUMBERS =
    IntStream.rangeClosed(32, Character.MAX_VALUE)
        .filter(Character::isLetterOrDigit)
        .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
        .toString();
public static String nextString() {
    return ThreadLocalRandom.current()
        .ints(stringLength, 0, ALL_LETTERS_AND_NUMBERS.length())
        .map(ALL_LETTERS_AND_NUMBERS::charAt)
        .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
        .toString();
}

2 Comments

I absolutely love your approach in answering. And this ThreadLocalRandom.current().ints(stringLength, 'A', 'Z'+1) you have no idea how many times I needed that until now. big thx
Well, I know, it can be easily adapted to pick from a List or array as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.