android,如何在应用程序端生成较短版本的 uuid(13 个字符)
android, how to generate shorter version of uuid (13 chars) an app side
android 应用程序需要生成 13 个字符的 uuid。但这可能会增加发生冲突的机会。
想出这个函数,思路是加上uuid的most/least SignificantBits,然后从Long中取出字符串。然后从结果中找出 13 字节长度的部分。测试 运行 似乎没有在单台机器上看到冲突(+100,000 个 uuids)。
但不确定跨机器冲突的可能性。
有没有更好的方法生成13个字符的uuid和合理的低分类率?
val random = Random()
fun generateUUID() {
val uuid: UUID = UUID.randomUUID()
val theLong = if (random.nextBoolean()) {
uuid.mostSignificantBits + uuid.leastSignificantBits
} else {
uuid.leastSignificantBits + uuid.mostSignificantBits
}
return java.lang.Long.toString(theLong, Character.MAX_RADIX)
}
不再是严格意义上的UUID; UUID 描述了一个非常具体的数据结构。使用适当 UUID 的低位通常不是一个好主意;这些从来都不是独一无二的。单机测试无定论。
编辑:现在我想起来了,问题中的"char"究竟是什么?十进制数?十六进制数字?一个字节?一个 ASCII 字符? 一个 Unicode 字符? 如果是后者,你可以在那里填充一个完整的正确 UUID。只需将其表示为二进制,而不是十六进制字符串。 UUID 的长度为 128 位。一个 Unicode 代码点是 20 位,因此其中 13 位将覆盖 260 位,这就足够了。
Java char
数据类型实际上略小于 16 位。如果“13 个字符”是指长度为 13 的 Java 字符串(或 13 个字符的数组),您仍然可以在那里填充 UUID,并使用一些技巧来避免保留的 UTF-16 代理项对值。
综上所述,为了生成全球唯一的 ID,他们通常使用当前时间、随机数和某种设备特定标识符的组合,将它们哈希在一起。这就是 canonical UUIDs 的工作原理。根据大小限制的确切性质(问题中含糊不清),建议使用不同的哈希算法。
编辑:关于使用整个 Unicode 范围。首先:您确实意识到 "du3d2t5fdaib4" 和“8efc9756-70ff-4a9f-bf45-4c693bde61a4”都是 hex 字符串,对吗?他们只使用 16 个字符,0-9 和 a-f?第二个破折号可以安全地省略,它们只是为了便于阅读。同时,单个 Java char
可以有 63488 个可能值之一——从 0 到 0xFFFF 的任何代码点,子范围 0xD800..0xDFFF 除外,都可以。包含所有这些疯狂字符的字符串不会好看,甚至无法打印;它可能看起来像“芦№Π║ثЯ”;某些字符可能不会显示在 Android 中,因为它们不在系统字体中,但它会是唯一的。
是否要求独特的字符串能够很好地显示?
如果没有,让我们看看。一个 UUID 是两个 64 位 Java long
。它是 Java 中的签名数据类型;如果它没有签名会更容易,但没有这样的事情。但是,我们可以将两个长整数视为 4 个整数,并确保整数是正数。
现在我们有 4 个正整数可以填充到 13 个字符中。我们也不想搞乱跨越变量边界的算术,所以让我们将每个整数转换成一个没有重叠的 3 字符块。这浪费了一些比特,但是哦,我们还有一些空闲。一个 int 是 4 个字节长,而 3 个 Java 个字符是 6 个字节长。
在排字时,我们要避开D800和DFFF之间的区域。此外,我们希望避免从 0 到 1F 的代码点 - 这些是控制字符,设计为不可打印。另外,让我们避免使用字符 0x20 - 即 space。现在,我不知道该字符串将如何使用; 它是否会在不允许转义的文本格式中使用,因此是否应避免某些其他字符以使下游的事情变得更简单。
连续的字符范围更容易处理,所以我们也完全放弃从 0xD800 开始的范围。这给我们留下了 0xD7DF 不同的代码点,从 0x21 开始。其中三个足以覆盖 32 位 int。将 int 转换为字符三元组的规则很简单:将 int 除以 0xD7DF 两次,取余数,将余数添加到基本代码点(即 0x21)。这个算法是你的香草"convert an int to a string in base N",知道不能超过三位数。
综合考虑,这里是Java:
public static String uuidToWeirdString(UUID uuid)
{
//Description of our alphabet: from 021 to 0xD7FF
final int ALPHA_SIZE = 0xD7DF, ALPHA_BASE = 0x21;
//Convert the UUID to a pair of signed, potentially negative longs
long low = uuid.getLeastSignificantBits(),
high = uuid.getMostSignificantBits();
//Convert to positive 32-bit ints, represented as signed longs
long []parts = {
(high >> 32) & 0xffffffff,
high & 0xffffffff,
(low >> 32) & 0xffffffff,
low & 0xffffffff
};
//Convert ints to char triples
int nPart, pos = 0;
char []c = new char[12];
for(nPart=0;nPart<4;nPart++)
{
long part = parts[nPart];
c[pos++] = (char)(ALPHA_BASE + part / (ALPHA_SIZE*ALPHA_SIZE));
c[pos++] = (char)(ALPHA_BASE + (part / ALPHA_SIZE ) % ALPHA_SIZE);
c[pos++] = (char)(ALPHA_BASE + part % ALPHA_SIZE);
}
return new String(c);
}
欣赏 Unicode 的美妙之处。
UUID 是一种 128 位数据类型,通常以 36 个字符的十六进制表示形式显示,或每个字符约 4 位。
你的例子是"du3d2t5fdaib4"。这只使用小写拉丁字母和阿拉伯数字,每个字符大约有 5 位,或 13×5=65 位。如果您还允许使用大写拉丁字母,那么每个字符大约有 6 位,即 13×6=78 位。
如果不丢弃将近一半的位,就无法将 128 位值放入 65 位或 78 位数据类型中,这会从根本上增加冲突的几率——甚至可能根据 UUID 的方式保证冲突已生成以及您丢弃了哪些位。
android 应用程序需要生成 13 个字符的 uuid。但这可能会增加发生冲突的机会。 想出这个函数,思路是加上uuid的most/least SignificantBits,然后从Long中取出字符串。然后从结果中找出 13 字节长度的部分。测试 运行 似乎没有在单台机器上看到冲突(+100,000 个 uuids)。 但不确定跨机器冲突的可能性。
有没有更好的方法生成13个字符的uuid和合理的低分类率?
val random = Random()
fun generateUUID() {
val uuid: UUID = UUID.randomUUID()
val theLong = if (random.nextBoolean()) {
uuid.mostSignificantBits + uuid.leastSignificantBits
} else {
uuid.leastSignificantBits + uuid.mostSignificantBits
}
return java.lang.Long.toString(theLong, Character.MAX_RADIX)
}
不再是严格意义上的UUID; UUID 描述了一个非常具体的数据结构。使用适当 UUID 的低位通常不是一个好主意;这些从来都不是独一无二的。单机测试无定论。
编辑:现在我想起来了,问题中的"char"究竟是什么?十进制数?十六进制数字?一个字节?一个 ASCII 字符? 一个 Unicode 字符? 如果是后者,你可以在那里填充一个完整的正确 UUID。只需将其表示为二进制,而不是十六进制字符串。 UUID 的长度为 128 位。一个 Unicode 代码点是 20 位,因此其中 13 位将覆盖 260 位,这就足够了。
Java char
数据类型实际上略小于 16 位。如果“13 个字符”是指长度为 13 的 Java 字符串(或 13 个字符的数组),您仍然可以在那里填充 UUID,并使用一些技巧来避免保留的 UTF-16 代理项对值。
综上所述,为了生成全球唯一的 ID,他们通常使用当前时间、随机数和某种设备特定标识符的组合,将它们哈希在一起。这就是 canonical UUIDs 的工作原理。根据大小限制的确切性质(问题中含糊不清),建议使用不同的哈希算法。
编辑:关于使用整个 Unicode 范围。首先:您确实意识到 "du3d2t5fdaib4" 和“8efc9756-70ff-4a9f-bf45-4c693bde61a4”都是 hex 字符串,对吗?他们只使用 16 个字符,0-9 和 a-f?第二个破折号可以安全地省略,它们只是为了便于阅读。同时,单个 Java char
可以有 63488 个可能值之一——从 0 到 0xFFFF 的任何代码点,子范围 0xD800..0xDFFF 除外,都可以。包含所有这些疯狂字符的字符串不会好看,甚至无法打印;它可能看起来像“芦№Π║ثЯ”;某些字符可能不会显示在 Android 中,因为它们不在系统字体中,但它会是唯一的。
是否要求独特的字符串能够很好地显示?
如果没有,让我们看看。一个 UUID 是两个 64 位 Java long
。它是 Java 中的签名数据类型;如果它没有签名会更容易,但没有这样的事情。但是,我们可以将两个长整数视为 4 个整数,并确保整数是正数。
现在我们有 4 个正整数可以填充到 13 个字符中。我们也不想搞乱跨越变量边界的算术,所以让我们将每个整数转换成一个没有重叠的 3 字符块。这浪费了一些比特,但是哦,我们还有一些空闲。一个 int 是 4 个字节长,而 3 个 Java 个字符是 6 个字节长。
在排字时,我们要避开D800和DFFF之间的区域。此外,我们希望避免从 0 到 1F 的代码点 - 这些是控制字符,设计为不可打印。另外,让我们避免使用字符 0x20 - 即 space。现在,我不知道该字符串将如何使用; 它是否会在不允许转义的文本格式中使用,因此是否应避免某些其他字符以使下游的事情变得更简单。
连续的字符范围更容易处理,所以我们也完全放弃从 0xD800 开始的范围。这给我们留下了 0xD7DF 不同的代码点,从 0x21 开始。其中三个足以覆盖 32 位 int。将 int 转换为字符三元组的规则很简单:将 int 除以 0xD7DF 两次,取余数,将余数添加到基本代码点(即 0x21)。这个算法是你的香草"convert an int to a string in base N",知道不能超过三位数。
综合考虑,这里是Java:
public static String uuidToWeirdString(UUID uuid)
{
//Description of our alphabet: from 021 to 0xD7FF
final int ALPHA_SIZE = 0xD7DF, ALPHA_BASE = 0x21;
//Convert the UUID to a pair of signed, potentially negative longs
long low = uuid.getLeastSignificantBits(),
high = uuid.getMostSignificantBits();
//Convert to positive 32-bit ints, represented as signed longs
long []parts = {
(high >> 32) & 0xffffffff,
high & 0xffffffff,
(low >> 32) & 0xffffffff,
low & 0xffffffff
};
//Convert ints to char triples
int nPart, pos = 0;
char []c = new char[12];
for(nPart=0;nPart<4;nPart++)
{
long part = parts[nPart];
c[pos++] = (char)(ALPHA_BASE + part / (ALPHA_SIZE*ALPHA_SIZE));
c[pos++] = (char)(ALPHA_BASE + (part / ALPHA_SIZE ) % ALPHA_SIZE);
c[pos++] = (char)(ALPHA_BASE + part % ALPHA_SIZE);
}
return new String(c);
}
欣赏 Unicode 的美妙之处。
UUID 是一种 128 位数据类型,通常以 36 个字符的十六进制表示形式显示,或每个字符约 4 位。
你的例子是"du3d2t5fdaib4"。这只使用小写拉丁字母和阿拉伯数字,每个字符大约有 5 位,或 13×5=65 位。如果您还允许使用大写拉丁字母,那么每个字符大约有 6 位,即 13×6=78 位。
如果不丢弃将近一半的位,就无法将 128 位值放入 65 位或 78 位数据类型中,这会从根本上增加冲突的几率——甚至可能根据 UUID 的方式保证冲突已生成以及您丢弃了哪些位。