Java UUID Compress&解压缩

时间:2012-08-06 02:53:07

标签: java uuid

我想做以下事情......

a)将生成的UUID压缩为长度为8的字符串。

b)将压缩的UUID解压缩回原始UUID。

原因是我必须将UUID发送到合作伙伴系统,并且合作伙伴系统只接受UUID的8个字符,并且我不能请求更改合作伙伴系统。

所以,剩下的工作就是将UUID压缩到8个char字符串,然后在从伙伴系统返回消息时将其解压缩回原始UUID。

有什么想法吗?

感谢。

3 个答案:

答案 0 :(得分:7)

出于信息理论的原因,你所要求的是不可能的。

RFC 4122指定的UUID为128位,UUID objects in Java也是如此。

Java String可以为每个字符存储16位,为8 - char字符串创建。但是,并非所有位序列都是有效的UTF-16字符串,因此在8个字符中,您可以存储少于128位的信息。

因此,如果您将UUID压缩为有效的8个字符的字符串,则会丢失信息,因此通常无法将其解压缩以检索原始UUID。

您可能希望生成一个较短的字符串以用作唯一标识符。如果是,请参阅Generating 8-character only UUIDs

答案 1 :(得分:2)

实现url safe uuid压缩的最佳方法是在base64中对其进行编码

public class UUIDUtils {

  public static String compress(UUID uuid) {
    ByteBuffer bb = ByteBuffer.allocate(Long.BYTES * 2);
    bb.putLong(uuid.getMostSignificantBits());
    bb.putLong(uuid.getLeastSignificantBits());
    byte[] array = bb.array();
    return Base64.getEncoder().encodeToString(array);
  }

  public static UUID decompress(String compressUUID) {
    ByteBuffer byteBuffer = ByteBuffer.wrap(Base64.getDecoder().decode(compressUUID));
    return new UUID(byteBuffer.getLong(), byteBuffer.getLong());
  }


}

结果:6227185c-b25b-4497-b821-ba4f8d1fb9a1 - > YicYXLJbRJe4IbpPjR + 5oQ ==

答案 2 :(得分:0)

您可以将UUID转换为字符串,该字符串实际上是一个16位char 8个元素的序列,具体如下。

static String encodeUuid(final UUID id) {
  final long hi = id.getMostSignificantBits();
  final long lo = id.getLeastSignificantBits();
  return new String(new char[] {
    (char) ((hi >>> 48) & 0xffff), (char) ((hi >>> 32) & 0xffff),
    (char) ((hi >>> 16) & 0xffff), (char) ((hi       ) & 0xffff),
    (char) ((lo >>> 48) & 0xffff), (char) ((lo >>> 32) & 0xffff),
    (char) ((lo >>> 16) & 0xffff), (char) ((lo       ) & 0xffff)
  });
}

static UUID decodeUuid(final String enc) {
  final char[] cs = enc.toCharArray();
  return new UUID(
    (long) cs[0] << 48 | (long) cs[1] << 32 | (long) cs[2] << 16 | (long) cs[3],
    (long) cs[4] << 48 | (long) cs[5] << 32 | (long) cs[6] << 16 | (long) cs[7]
  );
}

这段代码看起来确实应该可以工作(自己试试here),并且可以使用UTF-8和UTF-16进行编码/解码,而且大部分时间都没有问题:

static boolean validate(final UUID id, final Charset cs) {
  final ByteBuffer buf = cs.encode(encodeUuid(id));
  final UUID _id = decodeUuid(cs.decode(buf).toString());
  return id.equals(_id);
}

public static void main(final String[] argv) {
  final UUID id = UUID.randomUUID();
  assert validate(id, StandardCharsets.UTF_8)  : "failed using utf-8";
  assert validate(id, StandardCharsets.UTF_16) : "failed using utf-16";
}

C:\dev\scrap>javac UuidTest.java

C:\dev\scrap>java -ea UuidTest

但确实存在一些UTF-16代码点被保留为代理的问题。如果发生这种情况,编码将无法正常工作,您将无法重建原始UUID。有关详细信息,请参阅上面的Mechanical snail的回复。


您可以通过UUID.randomUUID生成的编码UUID始终实际删除的唯一数据是variant(始终为2)和4用于version的位(始终为4)。

  

这些全局标识符存在不同的变体。此类的方法用于操作Leach-Salz变体,尽管构造函数允许创建任何UUID变体(如下所述)。

     

变体2(Leach-Salz)UUID的布局如下:最重要的长度包括以下无符号字段:   0xFFFFFFFF00000000 time_low
0x00000000FFFF0000 time_mid
0x000000000000F000 version
0x0000000000000FFF time_hi

     

最不重要的长整数由以下无符号字段组成:   0xC000000000000000 variant
0x3FFF000000000000 clock_seq
0x0000FFFFFFFFFFFF node

     

变体字段包含一个标识UUID布局的值。上述位布局仅对变量值为2的UUID有效,表示 Leach-Salz 变体。

     

版本字段包含一个描述此UUID类型的值。 UUID有四种不同的基本类型:基于时间,DCE安全性,基于名称和随机生成的UUID。这些类型的版本值分别为1,2,3和4.