如何将18个字符串转换为唯一ID?

时间:2009-11-25 15:15:50

标签: java algorithm hash

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

My String实际上是一个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

7 个答案:

答案 0 :(得分:11)

你不能满足这些要求。

18个字符(假设)大写和小写字母具有56 18 或约2.93348915×103 31 组合。这是(方式)超过64位中可用的近似1.84467441×10 19 组合。

更新:我的组合学错了,呵呵。但结果相同。

答案 1 :(得分:5)

只需创建一个将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]

答案 2 :(得分:4)

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

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

另一种可能性是放宽等价物为long的要求。 BigInteger怎么样?那会很容易。

答案 3 :(得分:2)

我假设这是一个不区分大小写的字母数字字符串,因此从集[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个简化表示连接到sinlde 14字节值
  3. 将此广告投放到您的最终数据值
  4. 显然,Java只有8字节long。因此,如果您必须使用long,则 NOT 可以唯一地映射字符串,除非有其他内容可以减少有效输入字符串的空间。

答案 4 :(得分:1)

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

这是ROWID的布局,

   OOOOOO-FFF-BBBBBB-RRR

其中O是ObjectID。 F是FileNo。 B是Block,R是行号。所有这些都是Base64编码的。你可以看到O&amp; B可以有36位,B&amp; R可以有18位。

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

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

答案 5 :(得分:0)

找到一种以与数据库不同的方式提取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( "\.", "" );
final long lowerValue = Long.parseLong( hexNum.substring( 1 ), 16 );
long upperNibble = Integer.parseInt( hexNum.substring( 0, 1 ), 16 );
if ( upperNibble >= 8 ) {
  //Catch Case where ROWID > 8F000000.0000.0000
  upperNibble -= 8;
  return -( 9223372036854775807L - ( lowerValue - 1 + ( upperNibble << 60 ) ) );
} else {
  return ( lowerValue + ( upperNibble << 60 ) );
}

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

String s = Long.toHexString( featureID );
//Place 0's at the start of the String making a Strnig of size 16
s = StringUtil.padString( s, 16, '0', true ); 
StringBuffer sb = new StringBuffer( s );
sb.insert( 8, '.' );
sb.insert( 13, '.' );

return sb.toString();

为所有回复干杯。

答案 6 :(得分:-1)

这听起来...... icky,但我不知道你的背景所以试图不通过判断。 8)

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

ADDENDUM:当然需要截断半超级字符以适应,这听起来像是评论中的一个选项。