我一直在寻找一种简单的Java算法来生成伪随机字母数字字符串。在我的情况下,它将被用作唯一的会话/密钥标识符,“很可能”在超过50万代的时间内是唯一的(我的需求实际上不需要更复杂的东西)。

理想情况下,我可以根据我的独特性需求指定长度。例如,生成的长度为12的字符串可能看起来像“AEYGF7K0DM1X”。


当前回答

我开发了一个应用程序,为我的项目开发一个自动生成的字母数字字符串。在这个字符串中,前三个字符是字母,后七个字符是整数。

public class AlphaNumericGenerator {

    public static void main(String[] args) {
        java.util.Random r = new java.util.Random();
        int i = 1, n = 0;
        char c;
        String str = "";
        for (int t = 0; t < 3; t++) {
            while (true) {
                i = r.nextInt(10);
                if (i > 5 && i < 10) {

                    if (i == 9) {
                        i = 90;
                        n = 90;
                        break;
                    }
                    if (i != 90) {
                        n = i * 10 + r.nextInt(10);
                        while (n < 65) {
                            n = i * 10 + r.nextInt(10);
                        }
                    }
                    break;
                }
            }
            c = (char)n;

            str = String.valueOf(c) + str;
        }

        while(true){
            i = r.nextInt(10000000);
            if(i > 999999)
                break;
        }
        str = str + i;
        System.out.println(str);
    }
}

其他回答

这里有一个简单的一行代码,使用UUID作为字符基础,可以指定(几乎)任何长度。(是的,我知道以前有人建议使用UUID。)

public static String randString(int length) {
    return UUID.randomUUID().toString().replace("-", "").substring(0, Math.min(length, 32)) + (length > 32 ? randString(length - 32) : "");
}

使用UUID是不安全的,因为UUID的一部分根本不是随机的。erickson的过程非常简单,但它不会创建相同长度的字符串。以下代码片段应该足够了:

/*
 * The random generator used by this class to create random keys.
 * In a holder class to defer initialization until needed.
 */
private static class RandomHolder {
    static final Random random = new SecureRandom();
    public static String randomKey(int length) {
        return String.format("%"+length+"s", new BigInteger(length*5/*base 32,2^5*/, random)
            .toString(32)).replace('\u0020', '0');
    }
}

为什么选择长度*5?让我们假设一个长度为1的随机字符串的简单情况,即一个随机字符。要获得包含所有数字0-9和字符a-z的随机字符,我们需要一个介于0和35之间的随机数来获得每个字符中的一个。

BigInteger提供了一个构造函数来生成一个随机数,均匀分布在0到(2^numBits-1)的范围内。不幸的是,35不是一个可以由2^numBits-1接收的数字。

所以我们有两个选择:要么选择2^5-1=31,要么选择2^ 6-1=63。如果我们选择2^6,我们会得到很多“不必要”/“更长”的数字。因此,2^5是更好的选择,即使我们丢失了四个字符(w-z)。现在要生成特定长度的字符串,我们可以简单地使用2^(length*numBits)-1数字。最后一个问题是,如果我们想要一个具有一定长度的字符串,随机可能会生成一个小数字,因此长度不符合要求,因此我们必须将字符串填充到所需的长度,并加上前缀零。

这里是一个基于流的Java8解决方案。

    public String generateString(String alphabet, int length) {
        return generateString(alphabet, length, new SecureRandom()::nextInt);
    }

    // nextInt = bound -> n in [0, bound)
    public String generateString(String source, int length, IntFunction<Integer> nextInt) {
        StringBuilder sb = new StringBuilder();
        IntStream.generate(source::length)
                .boxed()
                .limit(length)
                .map(nextInt::apply)
                .map(source::charAt)
                .forEach(sb::append);

        return sb.toString();
    }

像这样使用

String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
int length = 12;
String generated = generateString(alphabet, length);
System.out.println(generated);

函数nextInt应该接受一个int绑定,并返回一个介于0和绑定-1之间的随机数。

Java提供了一种直接实现这一点的方法。如果你不想要破折号,它们很容易去掉。只需使用uuid.replace(“-”,“”)

import java.util.UUID;

public class randomStringGenerator {
    public static void main(String[] args) {
        System.out.println(generateString());
    }

    public static String generateString() {
        String uuid = UUID.randomUUID().toString();
        return "uuid = " + uuid;
    }
}

输出

uuid = 2d7428a6-b58c-4008-8575-f05549f16316

我使用的是一个非常简单的Java8解决方案。只需根据您的需求进行定制。

...
import java.security.SecureRandom;
...

//Generate a random String of length between 10 to 20.
//Length is also randomly generated here.
SecureRandom random = new SecureRandom();

String sampleSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";

int stringLength = random.ints(1, 10, 21).mapToObj(x -> x).reduce((a, b) -> a).get();

String randomString = random.ints(stringLength, 0, sampleSet.length() - 1)
        .mapToObj(x -> sampleSet.charAt(x))
        .collect(Collector
            .of(StringBuilder::new, StringBuilder::append,
                StringBuilder::append, StringBuilder::toString));

我们可以使用它生成如下的字母数字随机字符串(返回的字符串将强制包含一些非数字字符以及一些数字字符):

public String generateRandomString() {
            
    String sampleSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";
    String sampleSetNumeric = "0123456789";
    
    String randomString = getRandomString(sampleSet, 10, 21);
    String randomStringNumeric = getRandomString(sampleSetNumeric, 10, 21);
    
    randomString = randomString + randomStringNumeric;
    
    //Convert String to List<Character>
    List<Character> list = randomString.chars()
            .mapToObj(x -> (char)x)
            .collect(Collectors.toList());
    
    Collections.shuffle(list);
    
    //This is needed to force a non-numeric character as the first String
    //Skip this for() if you don't need this logic

    for(;;) {
        if(Character.isDigit(list.get(0))) Collections.shuffle(list);
        else break;
    }
    
    //Convert List<Character> to String
    randomString = list.stream()
            .map(String::valueOf)
            .collect(Collectors.joining());
    
    return randomString;
    
}

//Generate a random number between the lower bound (inclusive) and upper bound (exclusive)
private int getRandomLength(int min, int max) {
    SecureRandom random = new SecureRandom();
    return random.ints(1, min, max).mapToObj(x -> x).reduce((a, b) -> a).get();
}

//Generate a random String from the given sample string, having a random length between the lower bound (inclusive) and upper bound (exclusive)
private String getRandomString(String sampleSet, int min, int max) {
    SecureRandom random = new SecureRandom();
    return random.ints(getRandomLength(min, max), 0, sampleSet.length() - 1)
    .mapToObj(x -> sampleSet.charAt(x))
    .collect(Collector
        .of(StringBuilder::new, StringBuilder::append,
            StringBuilder::append, StringBuilder::toString));
}