有没有一个用于无符号数类型包装器的Java库?

32
显然,Java本身不支持无符号数类型,而且这种情况在短期内不会改变(从2002年开始有评论)。然而,在处理数据库(如MySQL)时,偶尔可能会用到无符号数。有很多关于如何模拟无符号数的问题。例如:

所有这些问题都只是表面上描述了如何实现。但是,是否有任何库能够全面实现适用于UByteUShortUIntegerULong的合适包装器呢?最好这些包装器能够扩展java.lang.Number并提供类似于java.math.BigInteger的算术API。

this document所示,有很多需要考虑的事情,也有很多可能出错的地方(例如如何进行位移操作,如何进行乘法等),所以我不想自己去做。另外,我也不想只是使用更高的类型(例如使用Short代替Byte等)。我希望保留8位16位32位64位数字的概念,以便与数据库等进行最佳交互。 更新: 在回答之前,请考虑到我已经了解了所有的解决方法,但我真的非常希望拥有这四种具有上述属性的类型。也许已经有人做过了,所以我才会提问。不需要提醒我解决方法。

4
玩转无符号数却动用位操作技巧时所需注意的所有细节,再加上没有“像数字一样运作”的数值类型,这些通常足以让我望而却步。Java 在某些领域确实存在疏漏,本文就是其中之一。 - user166390
@pst:我感觉一样。在大多数情况下,我也会被拒绝。但在这种情况下,这样的库确实是一种享受。 - Lukas Eder
1
前段时间我遇到了javolution库,其中包含了实现8位、16位、32位无符号整数的Struct类。 - 87element
@Lukas,除非你需要无符号64位,否则只需使用long就可以了。 - bestsss
1
@A.H. 我知道OpenHMS的SqlBuilder。不过它似乎已经失去了动力。你可以在这里找到更多类似的工具:https://dev59.com/qXM_5IYBdhLWcg3wzmrL - Lukas Eder
显示剩余11条评论
5个回答

35

当我需要在jOOQ中实现此功能时,我没有找到类似的内容,因此我自己开发了一个名为jOOU(U代表Unsigned)的开源库:

http://github.com/jOOQ/jOOU

我知道有些人可能认为这太过于复杂,但我真的想要精确地封装其他语言中称为ubyteushortuintulong的内容。希望通过Valhalla,这些封装可以变成值类型。

当然,欢迎为算术/位运算实现做出贡献!


20

没有人按照你想要的方式创建这些包装器,这是有一些原因的。

  • 性能
  • 垃圾收集器开销
  • 没有自动装箱/拆箱
  • 接口不好/无用
  • 存在更简单的处理方式

前四个点可以通过一个小的C示例来说明:

unsigned int x=42, y, m=5, t=18;
y = x * m + t;

这需要翻译成:

UInteger m = new UInteger(5);
UInteger t = new UInteger(18);
UInteger x = new UInteger(42);

UInteger y = x.multiplyBy(m);
y = y.add(t);

需要翻译的内容如下:

需要创建几个包装对象,multiplyByadd将生成更多。如果这种方式进行了许多计算,将会给垃圾回收器带来很大负担。包装和解包也会浪费CPU资源。

即使是简单的算术运算,编写或阅读起来也很麻烦是显而易见的。

出于同样的原因,没有人使用有符号的包装类型进行算术运算。

如果您使用比当前有符号类型要大一级的数据类型进行计算,并像这样截断上部分,则所有这些都是不必要的:

long x=42, y, m=5, t=18
y = (x*m + t) & 0xFFFFFFFF;

使用下一个最大的有符号类型也可以在Java和数据库之间进行转换。由于JDBC不会创建这些无符号包装类型,您必须自己完成这项工作,然后将数据转换为无符号包装器。

我自己进行了一些CPU密集型数据处理,并处理了二进制协议。在这些情况下,我希望也有无符号数据类型。但是在Java中使用包装器类型模拟它们比直接处理每个单独的情况更具问题。


2
我同意,当二进制协议使用无符号64位整数时,它只会变得“丑陋”.. 你好BigInteger :( - Voo
你有很好的观点。另一方面,有时你确实需要 BigInteger 和/或 BigDecimal。那么你别无选择,只能使用那个冗长的API。出于同样的原因,如果你确实需要 UInteger,为什么不使用它(如果有的话)?如果你想要速度,那么你仍然可以自己进行计算...我目前正在评估是否要为 http://www.jooq.org 使用这样的包装器,或者是否有其他方法...在我的情况下,速度可能不是一个问题。 - Lukas Eder
@LukasEder 你知道你仍然可以使用 BigInteger 包装器(对于 64 位无符号数)和下一个更大的值来进行所有其他运算,然后告诉数据库正确地存储这些值吗?我相当确定至少有一些数据库可以这样做。 - Voo
@Voo:不是很有趣 :-) JDBC的PreparedStatement.setString()ResultSet.getString()可以用于至少12个数据库...我已经实现了简单的包装器。只是想知道是否有人在此之前已经完成了它... - Lukas Eder
你不需要为无符号类型编写一个完整的类,因为基本操作如加法、减法、非扩展乘法和所有位运算(除了右移)对于有符号和无符号类型是相同的。对于剩余的操作,你可以编写一个函数来处理。 - phuclv
显示剩余5条评论

3

由于某些原因,Guava 没有 ushortubyte 类型。特别是 ubyte 最有用。你知道为什么吗? - Lukas Eder
有一个关于 short 的问题已经被提出:https://code.google.com/p/guava-libraries/issues/detail?id=670,而对于 byte,则有 UnsignedBytes - Andrejs
是的,但是UnsignedBytes仅提供了用于byte操作的静态方法... - Lukas Eder

2

谢谢,我知道所有这些部分解决方案。许多库已经朝着无符号数类型迈出了1-2步。但我真正寻找的是一个完全按照我在问题中描述的方式进行操作的库,包括算术运算... - Lukas Eder

2
    // Java 8
    int vInt = Integer.parseUnsignedInt("4294967295");
    System.out.println(vInt); // -1
    String sInt = Integer.toUnsignedString(vInt);
    System.out.println(sInt); // 4294967295

    long vLong = Long.parseUnsignedLong("18446744073709551615");
    System.out.println(vLong); // -1
    String sLong = Long.toUnsignedString(vLong);
    System.out.println(sLong); // 18446744073709551615

    // Guava 18.0
    int vIntGu = UnsignedInts.parseUnsignedInt(UnsignedInteger.MAX_VALUE.toString());
    System.out.println(vIntGu); // -1
    String sIntGu = UnsignedInts.toString(vIntGu);
    System.out.println(sIntGu); // 4294967295

    long vLongGu = UnsignedLongs.parseUnsignedLong("18446744073709551615");
    System.out.println(vLongGu); // -1
    String sLongGu = UnsignedLongs.toString(vLongGu);
    System.out.println(sLongGu); // 18446744073709551615

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接