4

YouTube 上的每个视频都有一个唯一的标识字符串,例如 1cru2fzUlEc。

是否有任何 Java 方法可以生成接近它的东西?关闭我的意思是字符串是唯一的,短的,并且使用数字和字母(区分大小写)。

我需要以与 YouTube 相同的方式使用这样的字符串:识别后端系统中的记录。我正在做一个 Java Web 应用程序。我不想使用http://example.com?id=123的方法。

我知道 Java 的 UUID 实现可以产生类似的结果,但与 YouTube 相比它太长了。

谢谢!

编辑1:

非常感谢大家的回复。您的所有输入都是有用的!似乎没有完美的解决方案。任何完美的东西(如果不是 UUID)都必须生成和检查(以避免重复)。我对吗?

我可以肯定地说 YouTube 在生成自己的 12 个字符的视频字符串时面临与我们 Java 人员相同的问题吗?

干杯!

编辑2:

我想使用全范围的字母数字字符,而不仅仅是十六进制数字。我将使用 Marcus Junius Brutus 的解决方案。我觉得它足够直观和安全。从理论上讲,我必须检查每个生成的字符串,但我不会这样做,因为每次检查都是另一个数据库调用。我将向生成的字符串 ID 的表字段添加唯一约束。我会让那个不幸的用户在他第一次生成记录时失败。他需要做的是返回表单再次填写并保存(希望不会因为重复的字符串值而第二次失败)。最初我将使用 12 个字符的字符串,并且可以在需要时轻松增加长度。

我打算将此解决方案用于与同一个后端数据库通信的分布式 Web 应用程序,这意味着同一个应用程序有多个 JVM。

这是我的解决方案,我希望它会奏效。

    String sampleAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    Random random = new Random();
    char[] buf = new char[12];
    for (int i = 0 ; i < 12 ; i++)
        buf[i] = sampleAlphabet.charAt(random.nextInt(sampleAlphabet.length()));
    return new String(buf);

谢谢大家的回复。它们都是可以接受的解决方案。我真的很感激。

最好的给你们!

4

8 回答 8

4

你为什么不试试这个?它满足您的所有需求。

https://github.com/peet/hashids.java

于 2013-11-09T17:28:35.527 回答
3

我认为最好的方法是生成一个带有数字和字母的随机字符串,在使用它之前,确保它不存在于您的数据库中。如果是这样,只需生成另一个并再次检查,等等......

您不太可能两次生成相同的字符串(但可能)。

或者如您所说,您可以使用 Java 的 UUID 实现,但我猜它有点长。

于 2013-04-17T16:45:51.650 回答
2

您可以 Base64 编码自纪元以来的当前时间(以毫秒为单位):

byte[] bytes = String.valueOf(System.currentTimeMillis()).getBytes();
String s = new sun.misc.BASE64Encoder().encode(bytes); 

有关演示,请参阅https://ideone.com/f4cFy1 。

于 2013-04-17T16:45:44.943 回答
2

UUID 是 128 位值的十六进制表示(插入“-”作为标点符号,就像逗号或空格在十进制表示中用作千位分隔符一样)。您可以像往常一样生成 UUID,然后将 128 位值转换为更紧凑的表示形式,例如Base64Ascii85(又名 Base85),从而保留 UUID 的优点并缩短标识符。这将减少到 20 个字符(使用 Ascii85);不像 YouTube 的 id 那样紧凑,但从 UUID 的 36 个字符中节省了大量资金。

如果这仍然太长,请生成较少数量的随机字节(使用好的 PRNG)并转换为 Ascii85。在 Ascii85 中,每四个字节的数据生成 5 个字符。

编辑:在之前的评论中,我建议使用 UUID 的哈希值。这是它的工作原理。

  1. 确定要在代码中允许的字符。(假设它是 az、AZ 和 0-9,总共 62 个字符。)字符数是编码的基b
  2. 决定在 base - b编码中需要多少个字符L。计算可以用那么多字符表示的值的数量n = b  L。
  3. 生成一个 128 位 UUID 值v。将此视为 0 和N = 2 128之间的数字。
  4. 使用一个简单的散列函数(如这里描述的函数)将v散列为范围 [0, n )的值h。例如,您可以使用h = floor(( v * n ) / N )。(如果n -等效地,b - 是 2 的幂,这只是一个二进制移位操作。)
  5. 使用步骤 1 中选择的字符集将h转换为 base- b表示。
于 2013-04-17T16:49:15.517 回答
1

如果您想生成任意字符(例如,全范围的字母数字,而不仅仅是十六进制数字)甚至篡改它们的频率,请使用您想要的示例字符创建一个数组,然后:

String sampleAlphabet = "whatever";
Random random = new Random();
char[] bf = new char[length];
for (int i = 0 ; i < length ; i++)
    buf[i] = sampleAlphabet.charAt(random.nextInt(sampleAlphabet.length());
return new String(bf);

如果您愿意,可以使用SecureRandom以提高安全性。

于 2013-04-17T16:47:53.697 回答
1

UUID 通常是十六进制格式的 128 位数字。

那么最大的 128 位数字是2^128-12. 如果以十六进制表示将变为 32 位字符长度log(2^128)/log(16) = 32

您可以定义一个自定义基数(例如包含 0-9、az 和 AZ),它将变为基数 (62) 10+26+26(在此基数中区分大小写!)。

所以最大的 128 位数字将成为ceil(log(2^128)/log(62)) = 22位数长度。

如果它仍然很大,那么您应该使用较小的数字(不是 128 位的)。

于 2013-04-17T16:52:05.553 回答
1

生成一个随机字符使用这个函数

public static String generateKey(int length) {
    String alphabet
            = new String("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); //9
    int n = alphabet.length();

    String result = new String();
    Random r = new Random();
    for (int i = 0; i < length; i++) {
        result = result + alphabet.charAt(r.nextInt(n));
    }
    return result;
}
于 2016-08-29T09:29:19.943 回答
0

这是一个很好的方法来做你想做的事。length是你想要的 UUID 的长度。重要的是要注意,随着 UUID 的长度缩短,发生碰撞的机会也会增加(感谢 assylias 在评论中提到这一点)。在使用它之前,您绝对应该检查以确保它不存在于数据库中。如果是这样,那么只需生成另一个。

public String getUUID(int length)
{
    return UUID.randomUUID().toString().replaceAll("-", "").substring(0, length);
}
于 2013-04-17T16:37:00.027 回答