在Java中声明一个无符号整数

407

在Java中是否有声明无符号整数的方法?

或者也可以这样表达问题: Java中的无符号相当于什么?

只是告诉你我正在查看Java的String.hashcode()实现。如果整数为32位无符号整数,我想测试碰撞的可能性。


10
Java中没有无符号类型。 - Andrew Logvinov
1
这篇帖子可能会对你有所帮助 https://dev59.com/dm855IYBdhLWcg3wQx5L#4449161 - tusar
2
据我所知,似乎没有办法。 相关链接:https://dev59.com/YXRC5IYBdhLWcg3wCMnX - James Manning
15
这取决于你想要达到的目的。对于大多数情况,Java中的所有整数都是有符号的。但是,在一种特定情况下,你可以将有符号整数视为无符号的:可以使用 >>> 运算符而非 >> 进行右移操作,从而不进行符号扩展。 - Sergey Kalinichenko
显示剩余5条评论
10个回答

393

Java没有无符号整数的数据类型。

如果需要存储大量数据,您可以定义一个long而不是int

您也可以将有符号整数视为无符号整数使用。 二进制补码表示法 的好处是对于有符号和无符号整数,在二进制级别上执行大多数操作(例如加法、减法、乘法和左移)是相同的。 然而,一些操作(如除法、右移、比较和强制转换)是不同的。 自Java SE 8以来,在Integer类中的新方法允许您完全使用int数据类型执行无符号算术运算

在Java SE 8及更高版本中,您可以使用int数据类型表示无符号32位整数,最小值为0,最大值为2^32-1。使用Integer类将int数据类型用作无符号整数。已向Integer类添加了像compareUnsigned, divideUnsigned等静态方法以支持无符号整数的算术运算。请注意,当声明时,int变量仍然带有符号,但是现在可以通过使用Integer类中的这些方法进行无符号运算。

11
公平地说,对于许多项目来说,技术要求并不那么严格,因此您确实可以承担“浪费”内存的情况。 - Simeon Visser
6
我知道,我也理解Java的最初目的。但例如智能手机并没有额外的内存可用。据我所知,它们通常使用Java。但是好吧,我不想在Java程序员和其他人之间引发争端。 - Tomáš Zato
152
对我来说,这不仅是浪费金钱的问题。当你在位级别上工作时,无符号类型更容易处理。 - Cruncher
39
从Java 8开始,这个说法不再成立。在Java SE 8及更高版本中,您可以使用“int”数据类型来表示无符号32位整数,其最小值为0,最大值为2^32-1。请参见http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html和http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html。 - 8bitjunkie
7
@7SpecialGems: 我已更新答案,包含了那些信息。话虽如此,声明无符号整数或排除负值是不可能的,只能通过使用各种方法将“int”类型视为无符号来实现。 - Simeon Visser
显示剩余7条评论

85

int类型的值是有符号还是无符号取决于如何解释这些比特位 - Java将比特位解释为有符号值(它没有无符号基本类型)。

如果你有一个int值,想将其解释为无符号值(例如从DataInputStream读取一个你知道应该被解释为无符号值的int值),那么可以使用以下技巧。

int fourBytesIJustRead = someObject.getInt();
long unsignedValue = fourBytesIJustRead & 0xffffffffL;

注意:十六进制字面值应该是long字面值,而不是int字面值,因此要在结尾加上'L'。


3
对我来说,这是最好的答案... 我的数据来源于一个 NFC 卡片的 UID,它可以有 4 或 8 个字节... 在 4 个字节的情况下,我需要将其转换为无符号整数,并且我不能使用 ByteBuffer.getLong 因为它不是 64 位数据。谢谢。 - Loudenvier
1
为什么需要使用 long 呢?你不能只使用 0xFFFFFF 并保持 int 类型吗? - Displee
1
@Displee 仔细想一下。如果你将一个 int 和 32 个二进制位的 1 进行 AND 运算,为什么 Java 会以与原始值不同的方式解释你的新变量呢?顺便说一下,这里需要 8 个 F,而不是 6 个,因为 0xF=1111,你需要 32 个这样的 1。 - Jonathan Komar
为什么不直接执行 long unsignedValue = someObject.getInt();?将 int 值与 0xFFFFFFFFL 进行“ANDing”的好处是什么? - Mohammad Kholghi
另一个问题:为什么在 0xFFFFFFFFL 的末尾添加 "L"?0xFFFFFFFF 不已经足够了吗? - Mohammad Kholghi
2
@MohammadKholghi 将 int 强制转换为 long 会进行符号扩展(即,如果 int 是负数,则 long 也是负数)。实际上,这意味着 long 的所有高位都将设置为 1。如果您对 32 位进行掩码处理,则只会得到这 32 位,因此它始终为正数。末尾的 "L" 使其成为长整型字面量而不是整型。 - devyn

28

我们需要无符号数字来模拟MySQL的无符号TINYINTSMALLINTINTBIGINT,以便在jOOQ中使用。这就是为什么我们创建了jOOU,这是一个提供Java无符号整数包装类型的极简主义库。例如:

import static org.joou.Unsigned.*;

// and then...
UByte    b = ubyte(1);
UShort   s = ushort(1);
UInteger i = uint(1);
ULong    l = ulong(1);

所有这些类型都扩展自java.lang.Number,并可以转换为更高级的原始类型和BigInteger。希望这能帮到你。

(免责声明:我在这些库背后的公司工作)


这听起来非常方便!谢谢你提到。 :) - Lucas Sousa

9

对于无符号数字,您可以使用来自Guava library 的以下类:

它们支持各种操作:

  • 除以

目前似乎缺少的是字节移位运算符。如果需要这些运算符,可以使用Java中的BigInteger。


5
也许这就是你的意思吧?
long getUnsigned(int signed) {
    return signed >= 0 ? signed : 2 * (long) Integer.MAX_VALUE + 2 + signed;
}
  • getUnsigned(0) → 0
  • getUnsigned(1) → 1
  • getUnsigned(Integer.MAX_VALUE) → 2147483647
  • getUnsigned(Integer.MIN_VALUE) → 2147483648
  • getUnsigned(Integer.MIN_VALUE + 1) → 2147483649
这段文本展示了一些数值的转换结果,将有符号整数转换为无符号整数。

你为了懒惰地使用三元运算符而牺牲了千万分之一秒的性能时间,这不太好。(开玩笑) - rassa45
7
你真的认为 2 * (long) Integer.MAX_VALUE + 20x1_0000_0000L 更易于理解吗?那么,为什么不直接使用 return signed&0xFFFF_FFFFL; 呢? - Holger

5

使用 char 来表示16位无符号整数。


Char并不是32位无符号整数,但char对于节省内存来说是一个好答案。 此链接: https://dev59.com/XHI-5IYBdhLWcg3weoFO (来自jqr的回答) - blobmaster

5
这里有很好的答案,但我没有看到任何位运算的演示。像Visser(目前被接受的答案)所说的那样,Java默认通过符号来表示整数(Java 8具有无符号整数,但我从未使用过)。不多说了,我们开始吧...
RFC 868示例
如果需要将无符号整数写入IO,会发生什么?实际示例是当您想要按照 RFC 868输出时间时。这需要一个32位大端无符号整数,它编码自1900年1月1日上午12:00以来的秒数。你该如何编码?
制作自己的无符号32位整数如下:
声明一个4字节(32位)的字节数组
Byte my32BitUnsignedInteger[] = new Byte[4] // represents the time (s)

这里是数组的初始化,参见 Java中的字节数组是否初始化为零?。现在你需要按照大端序(如果你想要造成混乱,也可以使用小端序)的顺序填充数组中的每个字节,假设你有一个包含时间的长整型变量(Java中的长整型变量长度为64位)叫做secondsSince1900(它只使用了前32位,并且你已经处理了Date引用1970年1月1日上午12:00的事实),那么你可以使用逻辑与运算符从中提取位,并将这些位移动到不会被忽略的位置(数字),以便在大端序中强制转换为Byte。

my32BitUnsignedInteger[0] = (byte) ((secondsSince1900 & 0x00000000FF000000L) >> 24); // first byte of array contains highest significant bits, then shift these extracted FF bits to first two positions in preparation for coersion to Byte (which only adopts the first 8 bits)
my32BitUnsignedInteger[1] = (byte) ((secondsSince1900 & 0x0000000000FF0000L) >> 16);
my32BitUnsignedInteger[2] = (byte) ((secondsSince1900 & 0x000000000000FF00L) >> 8);
my32BitUnsignedInteger[3] = (byte) ((secondsSince1900 & 0x00000000000000FFL); // no shift needed

我们的my32BitUnsignedInteger现在等同于一个符合RCF 868标准的无符号32位大端整数。是的,long数据类型是有符号的,但我们忽略了这个事实,因为我们假设secondsSince1900仅使用了低32位。由于将long强制转换为byte,所有高于2^7(十六进制中的前两个数字)的位都将被忽略。
参考来源:Java网络编程第四版。

这是一个新数组的Java实现吗?Byte my32BitUnsignedInteger[] = new Byte[4] // represents the time (s) 对我来说有点小疑惑。我记得应该这样写 Byte[] my32BitUnsignedInteger = new Byte[4] 如果我错了请纠正我。 - Yolomep
1
@Yolomep 是的,这是Java语法。在类型或名称后添加括号是可以的。 - Jonathan Komar
1
哇,我不知道呢。我以为那种数组声明只有C ++才有。 - Yolomep
1
你的 Bytes 是封装类。你想使用 byte[] array = new byte[4] - Antti Haapala -- Слава Україні

2

在使用数值之前,通过对这些值进行“逻辑与”运算,您似乎可以解决签名问题:

例如(byte[] header[0] 的值为0x86):

System.out.println("Integer "+(int)header[0]+" = "+((int)header[0]&0xff));

结果:

Integer -122 = 134

1

我刚写了这段代码,可以将“this.altura”的负数转换为正数。希望能帮到有需要的人。

       if(this.altura < 0){    

                        String aux = Integer.toString(this.altura);
                        char aux2[] = aux.toCharArray();
                        aux = "";
                        for(int con = 1; con < aux2.length; con++){
                            aux += aux2[con];
                        }
                        this.altura = Integer.parseInt(aux);
                        System.out.println("New Value: " + this.altura);
                    }

-28
你可以使用 Math.abs(number) 函数。它会返回一个正数。

17
苛刻要求:只要传入MIN_VALUE就可以了。 - Dennis Meng
3
@kyo722 我无法想象这会返回一个在无符号原始数据类型范围内的正值。 - Florian R. Klein
4
Nitpick #2: 只有当你传入0时才不会。 - genisage

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