我一直在寻找一个简单的Java算法来生成伪随机的字母数字字符串。在我的情况下,它将被用作唯一的会话/键标识符,可能在500K+次生成中是唯一的(我的需求并不需要更复杂的东西)。
理想情况下,我希望能够根据我的唯一性需求指定长度。例如,长度为12的生成字符串可能看起来像"AEYGF7K0DM1X"
。
我一直在寻找一个简单的Java算法来生成伪随机的字母数字字符串。在我的情况下,它将被用作唯一的会话/键标识符,可能在500K+次生成中是唯一的(我的需求并不需要更复杂的东西)。
理想情况下,我希望能够根据我的唯一性需求指定长度。例如,长度为12的生成字符串可能看起来像"AEYGF7K0DM1X"
。
令人惊讶的是,这里没有人提过,但是:
import java.util.UUID
UUID.randomUUID().toString();
很容易。
这样做的好处是UUID既漂亮又长,并且几乎不可能发生碰撞。维基百科有一个很好的解释:
"...每秒生成10亿个UUID,在接下来的100年中,仅创建一个重复项的概率约为50%。"
前4位是版本类型,后2位是变体类型,因此您可以获得122位随机数。所以如果您想要,可以从末尾截断以减小UUID的大小。虽然不建议这样做,但您仍然有足够的随机性,足以处理500k条记录。
Java 8 中的替代方法是:
static final Random random = new Random(); // Or SecureRandom
static final int startChar = (int) '!';
static final int endChar = (int) '~';
static String randomString(final int maxLength) {
final int length = random.nextInt(maxLength + 1);
return random.ints(length, startChar, endChar + 1)
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
}
public static String generateSessionKey(int length){
String alphabet =
new String("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); // 9
int n = alphabet.length(); // 10
String result = new String();
Random r = new Random(); // 11
for (int i=0; i<length; i++) // 12
result = result + alphabet.charAt(r.nextInt(n)); //13
return result;
}
import java.util.Random;
public class passGen{
// Version 1.0
private static final String dCase = "abcdefghijklmnopqrstuvwxyz";
private static final String uCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String sChar = "!@#$%^&*";
private static final String intChar = "0123456789";
private static Random r = new Random();
private static StringBuilder pass = new StringBuilder();
public static void main (String[] args) {
System.out.println ("Generating pass...");
while (pass.length () != 16){
int rPick = r.nextInt(4);
if (rPick == 0){
int spot = r.nextInt(26);
pass.append(dCase.charAt(spot));
} else if (rPick == 1) {
int spot = r.nextInt(26);
pass.append(uCase.charAt(spot));
} else if (rPick == 2) {
int spot = r.nextInt(8);
pass.append(sChar.charAt(spot));
} else {
int spot = r.nextInt(10);
pass.append(intChar.charAt(spot));
}
}
System.out.println ("Generated Pass: " + pass.toString());
}
}
我只是将密码添加到字符串中...没错,它运行良好。试一下吧...非常简单;我写的。
使用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');
}
}
为什么选择length*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
数字。最后一个问题是,如果我们想要一个特定长度的字符串,随机数可能会生成一个较小的数字,因此长度不符合要求,所以我们必须用前导零填充字符串到所需的长度。
/**
* Generate a random hex encoded string token of the specified length
*
* @param length
* @return random hex string
*/
public static synchronized String generateUniqueToken(Integer length){
byte random[] = new byte[length];
Random randomGenerator = new Random();
StringBuffer buffer = new StringBuffer();
randomGenerator.nextBytes(random);
for (int j = 0; j < random.length; j++) {
byte b1 = (byte) ((random[j] & 0xf0) >> 4);
byte b2 = (byte) (random[j] & 0x0f);
if (b1 < 10)
buffer.append((char) ('0' + b1));
else
buffer.append((char) ('A' + (b1 - 10)));
if (b2 < 10)
buffer.append((char) ('0' + b2));
else
buffer.append((char) ('A' + (b2 - 10)));
}
return (buffer.toString());
}
@Test
public void testGenerateUniqueToken(){
Set set = new HashSet();
String token = null;
int size = 16;
/* Seems like we should be able to generate 500K tokens
* without a duplicate
*/
for (int i=0; i<500000; i++){
token = Utility.generateUniqueToken(size);
if (token.length() != size * 2){
fail("Incorrect length");
} else if (set.contains(token)) {
fail("Duplicate token generated");
} else{
set.add(token);
}
}
}
根据您的需求更改字符串中的字符。
字符串是不可变的。在这里,StringBuilder.append
比字符串拼接更高效。
public static String getRandomString(int length) {
final String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_+";
StringBuilder result = new StringBuilder();
while(length > 0) {
Random rand = new Random();
result.append(characters.charAt(rand.nextInt(characters.length())));
length--;
}
return result.toString();
}
Random
实例是低效的。 - ericksonimport java.util.Date;
import java.util.Random;
public class RandomGenerator {
private static Random random = new Random((new Date()).getTime());
public static String generateRandomString(int length) {
char[] values = {'a','b','c','d','e','f','g','h','i','j',
'k','l','m','n','o','p','q','r','s','t',
'u','v','w','x','y','z','0','1','2','3',
'4','5','6','7','8','9'};
String out = "";
for (int i=0;i<length;i++) {
int idx=random.nextInt(values.length);
out += values[idx];
}
return out;
}
}
我不太喜欢关于“简单”解决方案的任何答案:S
我会选择一个简单的;), 纯Java, 一行代码(熵基于随机字符串长度和给定字符集):
public String randomString(int length, String characterSet) {
return IntStream.range(0, length).map(i -> new SecureRandom().nextInt(characterSet.length())).mapToObj(randomInt -> characterSet.substring(randomInt, randomInt + 1)).collect(Collectors.joining());
}
@Test
public void buildFiveRandomStrings() {
for (int q = 0; q < 5; q++) {
System.out.println(randomString(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")); // The character set can basically be anything
}
}
public String randomString(int length, String characterSet) {
StringBuilder sb = new StringBuilder(); // Consider using StringBuffer if needed
for (int i = 0; i < length; i++) {
int randomInt = new SecureRandom().nextInt(characterSet.length());
sb.append(characterSet.substring(randomInt, randomInt + 1));
}
return sb.toString();
}
@Test
public void buildFiveRandomStrings() {
for (int q = 0; q < 5; q++) {
System.out.println(randomString(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")); // The character set can basically be anything
}
}
但另一方面,您也可以选择使用UUID,它具有相当好的熵:
UUID.randomUUID().toString().replace("-", "")
我正在使用来自Apache Commons的库来生成字母数字字符串:
import org.apache.commons.lang3.RandomStringUtils;
String keyLength = 20;
RandomStringUtils.randomAlphanumeric(keylength);
它快速简单!
Long.toHexString(Double.doubleToLongBits(Math.random()));
生成一个16进制的随机字符串。UUID.randomUUID().toString();
生成一个随机唯一标识符。RandomStringUtils.randomAlphanumeric(12);
生成一个包含12个字符的随机字母数字组合。 - Manindar