How do I generate a random string?

Here is a static method that accepts a desired string length as an argument. It takes a full string of ASCII numbers and letters and loops through the index of your desired string length, randomly choosing an index in the full character string.

public static String generateRandomString(Integer len) {
    final String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
    String randStr = '';
    while (randStr.length() < len) {
       Integer idx = Math.mod(Math.abs(Crypto.getRandomInteger()), chars.length());
       randStr += chars.substring(idx, idx+1);
    }
    return randStr; 
}

You can create random strings by stringing together Integer values. Here's how I would perform such a task:

public static String randomStringAll(Integer count) {
    Integer[] chars = new Integer[0],
        offsets = new Integer[] { 48, 65, 97 },
            mods = new Integer[] { 10, 26, 26 };

    while(count > chars.size()) {
        Integer cat = Math.abs(Math.mod(Crypto.getRandomInteger(),3));
        chars.add(Math.mod(Math.abs(Crypto.getRandomInteger()), mods[cat])+offsets[cat]);
    }
    return String.fromCharArray(chars);
}

You can extend this to include any Unicode characters, but, of course, this is plain ASCII ("Latin") characters, a-z, A-Z, and 0-9.

Edit: Forgot to use absolute value. Fixed.

Edit: In response to the question/comment, I tried a simple benchmark (I'll come back and perform heavier analysis later).

I created two tests. The first test calls the function to make a string 100,000 large. All times are in MS.

    generateRandomString     randomStringAll
1   5369                     5165
2   4856                     4151
3   5754                     4447
4   4821                     4359
5   4985                     4171
Avg 5157                     4458.6

Then, I created a test where we requested 10,000 strings 10 characters long. All times are in MS.

1   3437                     4813
2   3102                     5030
3   3478                     5268
4   3180                     5270
5   3437                     4953
Avg 3326.8                   5066.8

Observations:

Repeated string concatenation seems to slow the system down at larger sizes. Crypto.getRandomInteger/getRandomLong is really expensive, so at smaller sizes, generateRandom string is superior, while at larger sizes, the lack of memory thrashing makes randomStringAll faster.

Based on these observations, I made further adjustments, and finally came up with:

public static String randomStringAllEnhanced2(Integer count) {
    Integer[] chars = new Integer[count],
        offsets = new Integer[] { 48, 65, 97 },
            mods = new Integer[] { 10, 26, 26 };

    while(count > 0) {
        Integer rnd = Math.abs(Crypto.getRandomInteger()), cat = Math.mod(rnd, 3), seed = Math.mod(rnd, mods[cat]);
        chars[(count--)-1] = seed+offsets[cat];
    }
    return String.fromCharArray(chars);
}

I ran the same tests, and came up with the following, first for 100k strings:

1       5369    2940
2       4856    3258
3       5754    2935
4       4821    2942
5       4985    3093
Avg     5157    3033.6

This method, taking advantage of static memory, and reducing the number of Crypto calls, made this function blazing fast (comparatively) for very large strings.

Then, I ran the second test, and came up with the following numbers:

1       3437    3218
2       3102    3425
3       3478    3465
4       3180    3696
5       3437    3931
Avg     3326.8  3547

Here, the winner appears to still be generateRandomString, but due to the few number of samples, a deviation of simply 200 ms is probably statistically insignificant (I'd say it takes at least 500 ms to be statistically interesting).

I'll probably run an extended batch of 1000 or more each through Visualforce or something to see the long-term gains/loss, but I think the point here is that generateRandomString is probably better if you need alphanumeric characters. If you need just a-z or A-Z, you can use these:

public static String randomStringUCAZ(Integer count) {
    Integer[] chars = new Integer[0];
    while(count > chars.size()) {
        chars.add(Math.mod(Crypto.getRandomInteger(),26)+65);
    }
    return String.fromCharArray(chars);
}
public static String randomStringLCAZ(Integer count) {
    Integer[] chars = new Integer[0];
    while(count > chars.size()) {
        chars.add(Math.mod(Crypto.getRandomInteger(),26)+97);
    }
    return String.fromCharArray(chars);
}

100k Strings were impressive:

1       1881
2       1867
3       1942
4       1774
5       1790
Avg     1850.8

10k 10 character strings were blazing:

1       2092
2       1991
3       2245
4       1971
5       1976
Avg     2055

After this experiment, I realized that we could try to combine what appeared to be faster features, and came up with:

public static String generateRandomString2(Integer len) {
    String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
    String[] result = new String[len];
    Integer idx = 0;

    while(idx < len) {
        Integer chr = Math.mod(Math.abs(Crypto.getRandomInteger()), 62);
        result[idx++] = chars.substring(chr, chr+1);
    }
    return String.join(result,'');
}

This method excels at longer strings than shorter, but shows little statistical difference between generateRandomString (but on average, faster, for a small sample).

100k Strings:

1       2842
2       2772
3       2707
4       2511
5       2641
Avg     2694.6

10k 10 character strings:

1       3034
2       3415
3       3367
4       3084
5       2798
Avg     3139.6

Finally, I tried creating a function that had a better "average" performance across big and small strings (all prior functions seemed to have some specific use), and the result was:

static Integer[] charset;

static {
    charset = new Integer[0];
    for(Integer i = 48; i < 58; i++) charset.add(i);
    for(Integer i = 65; i < 91; i++) charset.add(i);
    for(Integer i = 97; i < 123; i++) charset.add(i);
}

public static String genRndStrFast(Integer len) {
    Integer[] chars = new Integer[len];
    Integer key, size = charset.size();

    for(Integer idx = 0; idx < len; idx++) {
        chars[idx] = charset[Math.mod(Math.abs(Crypto.getRandomInteger()), size)];
    }

    return String.fromCharArray(chars);
}

Despite the extra initialization time (static variable), "caching" this data resulted in smoother performance:

100k strings:

1       2104
2       2077
3       2038
4       2276
5       2266
Avg     2152.2

10k 10 character strings:

1       2489
2       2056
3       2594
4       2402
5       2333
Avg     2374.8

Overall, there's little benefit to choosing one function over the other if you're calling it just once, but there is a benefit in deciding which algorithm to use depending on how long of a string you need or when you need to generate many strings. This answer originally was simply meant to be an alternative function, but actually yielded tons of useful information.


From your original, proposal, you can expand the AES key and use base64encode to get a wider range of characters. Doing:

Blob blobKey = crypto.generateAesKey(192);
String key = EncodingUtil.base64encode(blobKey);
return key.substring(0,len);

Gets you a string of the same length but with 64 different characters instead of 16. It also runs much faster than manually using loops and indexes to create your string (for 1000 x 32 char strings, I timed 158 ms, vs 5535 ms for manually).

(Btw, here's the template I use for comparing algorithm performances on Salesforce. http://pastebin.com/XDAtu4ip)