Java中随机字符串生成:从混乱到高性能

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

如何在Java中生成随机字符串而不抓狂?

\n\n

想象一下:你在编写网站注册功能。需要为邮箱验证生成唯一令牌。或者为新用户生成密码。又或者只是为链接生成一个可读的ID。首先想到的是使用UUID.randomUUID(),去掉连字符,然后万事大吉。

\n\n

等等。你知道吗,这个看似简单的操作可能会成为性能的瓶颈?Stack Overflow上"How to generate a random alpha-numeric string"这个问题之所以获得数千次浏览,绝非偶然。魔鬼总是藏在细节中:随机数生成器的选择、算法、内存操作。在本文中,我们不仅要编写代码——还要将其优化到极致。

\n\n

准备好IntelliJ IDEA。我们开始吧!

\n\n

1. 最简单(但最慢)的方法:UUID

\n\n

从经典方法开始。UUID生成一个128位的值。如果去掉连字符,会得到一个32字符的十六进制字符串(0-9, a-f)。这已经是字母数字字符串了,但字母表有限。

\n\n
public static String generateWithUUID() {\n    return UUID.randomUUID().toString().replace("-", "");\n}\n
\n\n

优点:一行代码。无需思考。

\n

缺点:无法控制长度。仅限十六进制字符(16个字符的字母表)。用于密码——不够强。用于令牌——勉强可用,但有个问题。

\n\n

顺便说一句,UUID.randomUUID()底层使用了SecureRandom。这是加密安全的,但很慢。如果你每秒需要生成10,000个字符串——这个方法会让你失望。

\n\n

2. Apache Commons Lang中的RandomStringUtils

\n\n

这是一个从一个项目流传到另一个项目的经典方法。commons-lang3库提供了一个现成的方法:

\n\n
import org.apache.commons.lang3.RandomStringUtils;\n\npublic static String generateWithCommons(int length) {\n    return RandomStringUtils.randomAlphanumeric(length);\n}\n
\n\n

漂亮吗?是的。性能好吗?不太好。

\n

在底层,RandomStringUtils使用java.util.Random(非加密安全)并创建许多临时对象。对于大多数任务来说足够了,但如果你要生成数百万个字符串——准备好应对GC暂停。

\n\n

3. 使用Random.nextInt()的自定义方法

\n\n

让我们自己造个轮子。最直接的方法:

\n\n
import java.security.SecureRandom;\nimport java.util.Random;\n\npublic class RandomStringGenerator {\n    private static final String ALPHANUMERIC = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";\n    private static final int BASE = ALPHANUMERIC.length();\n    private final Random random;\n\n    public RandomStringGenerator() {\n        this.random = new SecureRandom(); // 为了加密安全性\n    }\n\n    public String generate(int length) {\n        StringBuilder sb = new StringBuilder(length);\n        for (int i = 0; i < length; i++) {\n            sb.append(ALPHANUMERIC.charAt(random.nextInt(BASE)));\n        }\n        return sb.toString();\n    }\n}\n
\n\n

这里重要的是什么?

\n
    \n
  • 我们使用已知容量的StringBuilder——不会有多余的扩容。
  • \n
  • SecureRandom保证输出不可预测。对于令牌——这是必须的。
  • \n
  • 字母表由字符串定义——62个字符(26+26+10)。可以添加特殊字符。
  • \n
\n\n

但有一个问题:在Linux上SecureRandom可能很慢(等待熵)。如果速度比安全性更重要——请使用ThreadLocalRandom.current()。

\n\n

4. 使用char[]和ThreadLocalRandom进行优化

\n\n

想要榨干性能?放弃StringBuilder,直接操作字符数组:

\n\n
import java.util.concurrent.ThreadLocalR                

博客

书籍推荐