Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

How does one generate a random string sequence with Apex? I need to generate a code when a particular object is created in our Salesforce environment. I'd like the code to contain numbers from 0-9 and letters from A-Z.

Searching their rather awful developer forums I found:

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

However this isn't going to cover a very large range of letters.

In the C# world something like the following would do the trick:

var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var result = new string(
    Enumerable.Repeat(chars, numberOfCharacters)
              .Select(s => s[random.Next(s.Length)])
              .ToArray()
);

Is there an easy way to do something similar in Apex that I'm just not familiar with?

share|improve this question

2 Answers 2

up vote 8 down vote accepted

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()), 62);
       randStr += chars.substring(idx, idx+1);
    }
    return randStr; 
}
share|improve this answer
    
If you can just provide a little narrative around the method then I'll mark this as accepted. The other answer was great, but your answer was correct and was first. I just think an explanation would be useful for those people who view the question later to make sure they understand how it works. –  Justin Helgerson Jul 11 at 4:18
    
I've added a brief explanation. –  greenstork Jul 11 at 15:57

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.

share|improve this answer
    
Do you view that your answer has an advantage over the other answer provided? I'm asking for my own sake and for the sake of future travelers that stumble upon this question. –  Justin Helgerson Jul 10 at 18:26
1  
@Ek0nomik See what you made me do? At least now, we know which has better performance... –  sfdcfox Jul 10 at 20:16
    
+1. Great edit and information, thanks for your help! –  Justin Helgerson Jul 10 at 21:13
    
You win the internet today sfcdfox, great research. –  greenstork Jul 10 at 21:26

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.