【问题标题】:How to convert an 18 Character String into a Unique ID?如何将 18 个字符串转换为唯一 ID?
【发布时间】:2010-12-20 08:03:44
【问题描述】:

我有一个 18 个字符的字符串,我需要将其转换为唯一的 long(在 Java 中)。 示例字符串为:AAA2aNAAAAAAADnAAA

我的字符串实际上是一个 Oracle ROWID,所以如果需要可以分解,请参阅: http://download-uk.oracle.com/docs/cd/B19306_01/server.102/b14220/datatype.htm#CNCPT713

生成的长数,(1)必须是唯一的,因为没有两个结果可以指向同一个数据库行;(2)必须是可逆的,所以我可以从长数中取回 ROWID 字符串?

欢迎对使用的算法提出任何建议。

几年前的 Oracle 论坛问题:http://forums.oracle.com/forums/thread.jspa?messageID=1059740

【问题讨论】:

  • 到现在这几乎是不可能的。如果它有来自哈希表的负载因子已经消失。但是,让如果有人有任何想法..

标签: java algorithm hash


【解决方案1】:

你不能,有这些要求。

18 个字符(假设)大小写字母有 5618 或大约 2.93348915 × 10331 组合。这比 64 位中可用的大约 1.84467441 × 1019 组合多(方式)。

更新:我的组合数错了,呵呵。还是一样的结果。

【讨论】:

  • 根据文档,它是 base 64 编码,使用 a-z、A-Z、0-9 以及 + 和 /。所以更糟糕的是:-)
  • 如果数字是允许的,那么让那个 18^((2*26)+10) 变得更糟。
  • 是的,但是 18 个字符的字符串可以分解成它的组件,所以我想知道是否可以为此做任何事情: AAA2aNAAAAAAADnAAA = AAA2aN - AAA - AAAADn - AAA 另外,唯一性保证实际上只需要涵盖最多 1 亿个案例....不太可能有比这更大的数据库表!
  • ehm,每个字符有 56 个选项,18 个字符,意味着有 56^18 种可能的组合,不是吗?
  • 我怀疑我们是否会超过现实世界中 18 位数字的数量限制(10 亿,甚至 Google 的索引页面也不超过 1000 亿)。所以从 String -> Number 的映射不应该有任何约​​束。
【解决方案2】:

只需创建一个映射(字典/哈希表),将 ROWID 字符串映射到(递增的)long。如果您保留两个这样的字典并将它们包装在一个不错的类中,您将在字符串和长 ID 之间进行双向查找。

伪代码:

class BidirectionalLookup:
    dict<string, long> stringToLong
    dict<long, string> longToString
    long lastId

    addString(string): long
        newId = atomic(++lastId)
        stringToLong[string] = newId
        longToString[newId] = string
        return newId

    lookUp(string): long
        return stringToLong[string]

    lookUp(long): string
        return longToString[long]

【讨论】:

  • 这是我之前实现的(经过初步调查 - 请参阅 Oracle 论坛链接)。问题是这个哈希映射缓存已经增长到超过哈希映射大小的上限!因此再次对其进行调查
  • 为什么不为此使用数据库中的表?
【解决方案3】:

代表 base 64 编码的 18 个字符的 String 代表总共 108 位信息,几乎是 long 的 64 位的两倍。如果我们想要表示每个可能的键并且有表示是可逆的。

字符串可以很容易地分解为 4 个数字。这 4 个数字中的每一个都代表着一些东西——一个块号,那个块中的一个偏移量,等等。如果您设法建立基础数量的上限,这样您就知道不会出现更大的数字(即,如果您找到一种方法来识别至少 44 个始终为 0 的位),那么您可以将其余部分映射到长的,可逆的。

另一种可能性是放宽对等值是long 的要求。 BigInteger 怎么样?这样就很容易了。

【讨论】:

  • “BigInteger 怎么样?”或两个多头。
  • 我简要地考虑了这一点,但是两个多头是令人讨厌的,IMO。我们正在使用 OO 语言,因此我们可以将单个值视为单个实体。对于足够小的数字,BigInteger 实际上是两个长整数,但它被包装成一个连贯的包。
  • 当然,只是我们不会做任何数学运算。我可能会定义一个包含两个long 字段(“tophalf”和“bottomhalf”或其他)的类以及与字符串相互转换的方法。但实际上这取决于提问者(认为他)为什么需要很长时间。如果他只有 8 个字节的存储空间,那么 BigInteger 和两个 long 都不可能。
【解决方案4】:

我假设这是一个不区分大小写的字母数字字符串,因此从集合[a-zA-Z0-9]*中提取

在这种情况下你有

26 + 26 + 10 = 62 

每个字符的可能值。

62 < 64 = 2^6

换句话说,您需要(至少)6 位来存储密钥的 18 个字符中的每一个。

6 * 18 = 108 bits 

唯一存储整个字符串。

108 bits  = (108 / 8) = 13.5 bytes.

因此,只要您的数据类型可以存储至少 13.5 个字节,那么您就可以相当简单地定义一个映射:

  1. 从每个字符的原始 ASCII 映射到仅使用 6 位的表示
  2. 将所有 18 种缩减表示连接成一个 14 字节的单字节值
  3. 将此转换为您的最终数据值

显然,Java 只有一个 8 字节的 long。因此,如果您必须使用long,那么不可能唯一地映射字符串,除非有其他东西可以减少有效输入字符串的空间。

【讨论】:

  • 它实际上是一个base 64编码,所以它还包括+/
  • 好的,尽管如此,它仍然可以容纳每个字符 6 位
【解决方案5】:

理论上,您不能用长(8 个字节)表示 ROWID。但是,根据您的数据库(整个服务器,而不仅仅是您的表)的大小,您可能能够将其编码为 long.

这是ROWID的布局,

   OOOOOO-FFF-BBBBBB-RRR

其中 O 是 ObjectID。 F 是文件号。 B 是块,R 是行号。它们都是 Base64 编码的。如您所见,O&B 可以有 36 位,而 B&R 可以有 18 位。

如果您的数据库不是很大,您可以为每个部分使用 2 个字节。基本上,您的 ObjectId 和块号将被限制为 64K。我们的 DBA 认为我们的数据库必须大几个数量级才能接近这些限制。

我建议你在数据库中找到每个部分的最大值,看看你是否接近。如果它们接近极限,我不会使用 long。

【讨论】:

  • 在创建表后很长时间插入新行可能会导致新行的 ROWID 完全不同,因此我们不能真正走这条路。
【解决方案6】:

找到了一种以不同方式从数据库中提取 ROWID 的方法....

SQL> select DBMS_ROWID.ROWID_TO_RESTRICTED(ROWID, 1) FROM MYTABLE;

0000EDF4.0001.0000 0000EDF4.0002.0000 0000EDF4.0004.0000 0000EDF4.0005.0000 0000EDF4.0007.0000 0000EDF5.0000.0000 0000EDF5.0002.0000 0000EDF5.0003.0000

然后将其转换为如下数字:

final String hexNum = rowid.replaceAll("\.", ""); 最终的 long lowerValue = Long.parseLong( hexNum.substring( 1 ), 16 ); long upperNibble = Integer.parseInt(hexNum.substring(0, 1), 16); if ( 上半字节 >= 8 ) { //捕获 ROWID > 8F000000.0000.0000 的情况 上半字节-= 8; 返回 -( 9223372036854775807L - ( lowerValue - 1 + ( upperNibble

然后将该数字反转回字符串格式,如下所示:

字符串 s = Long.toHexString(featureID); //将 0 放在字符串的开头,形成大小为 16 的字符串 s = StringUtil.padString(s, 16, '0', true ); StringBuffer sb = new StringBuffer( s ); sb.insert(8, '.'); sb.insert(13, '.');

return sb.toString();

为所有回复干杯。

【讨论】:

    【解决方案7】:

    这听起来……很恶心,但我不知道你的背景,所以尽量不要通过判断。 8)

    您是否考虑过将字符串中的字符转换为对应的 ASCII 字符?

    附录:当然需要截断半多余的字符以适应,这听起来像是 cmets 的一个选项。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-21
    • 2019-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多