在Java中有没有像(My)SQL中使用无符号数字的方式?
例如:我想使用一个8位变量(byte
),其范围为:0
... 256
;而不是-128
... 127
。
在Java中有没有像(My)SQL中使用无符号数字的方式?
例如:我想使用一个8位变量(byte
),其范围为:0
... 256
;而不是-128
... 127
。
不,Java除了char
(有效值为0-65535)之外没有任何无符号原始类型。这很麻烦(特别是对于byte
),但这就是现实。
通常你要么保持相同的大小,并将“高”数字溢出为负数,要么使用更宽的类型(例如short
代替 byte
)并处理额外的内存需求。
public class UInt8 implements Comparable<UInt8>,Serializable
{
public static final short MAX_VALUE=255;
public static final short MIN_VALUE=0;
private short storage;//internal storage in a int 16
public UInt8(short value)
{
if(value<MIN_VALUE || value>MAX_VALUE) throw new IllegalArgumentException();
this.storage=value;
}
public byte toByte()
{
//play with the shift operator ! <<
}
//etc...
}
在内部,您不应使用较小的值--只需使用int。据我所知,使用较小的单位除了减慢速度外没有任何作用。它不会节省内存,因为Java在内部使用系统的字大小进行所有存储(它不会打包字)。
但是,如果您使用较小的存储单元,则必须为每个操作进行掩码或范围检查或其他操作。
有没有注意到char(任何操作)char产生int?他们真的不希望你使用这些其他类型。
例外情况是数组(我相信将被打包)和I/O,在这些情况下,您可能会发现使用较小的类型很有用...但掩码也可以工作。
不行,你不能改变那个。如果你需要比127更大的东西,就选择比一个字节更大的东西。
我对Java和编程都很新手。然而,最近我遇到了需要使用无符号值的情况。
我花了大约两周的时间编写我所想的一切,但我是一个完全的新手,所以你可能会花费更少的时间。
总体思路是创建一个接口,我将其命名为:UnsignedNumber<Base, Shifted>
,并扩展Number.class,同时实现一个抽象的AbstractUnsigned<Base, Shifted, Impl extends AbstractUnsigned<Base, Shifted, Impl>>
类。
因此,Base参数化类型表示基本类型,Shifted表示实际的Java类型。Impl是这个抽象类的实现的快捷方式。
大部分时间都消耗在Java 8 Lambdas和内部私有类以及安全程序上。重要的是要在数学运算(如减法或负加法)产生零限制时实现无符号的行为:向后溢出上限签名。
最后,又花了几天时间编写工厂和实现子类。
到目前为止,我已经知道:
UByte和MUByte UShort和MUShort UInt和MUInt ...等等。它们是AbstractUnsigned的后代:
UByte或MUByte扩展AbstractUnsigned<Byte,Short,UByte>
或AbstractUnsigned<Byte,Short,MUByte>
UShort或MUShort扩展AbstractUnsigned<Short,Integer,UShort>
或AbstractUnsigned<Short,Integer,MUShort>
...等等。
总体思路是将无符号上限作为移位(强制转换)类型,并将负值的代码转置为它们不是来自零,而是无符号上限。
更新: (感谢Ajeans的友好和礼貌指导)
/**
* Adds value to the current number and returns either
* new or this {@linkplain UnsignedNumber} instance based on
* {@linkplain #isImmutable()}
*
* @param value value to add to the current value
* @return new or same instance
* @see #isImmutable()
*/
public Impl plus(N value) {
return updater(number.plus(convert(value)));
}
这是一个外部可访问的方法,属于AbstractUnsigned<N, Shifted, Impl>
(或者之前被称为AbstractUnsigned<Base, Shifted, Impl>
);
现在,让我们来看看底层的工作:
private Impl updater(Shifted invalidated){
if(mutable){
number.setShifted(invalidated);
return caster.apply(this);
} else {
return shiftedConstructor.apply(invalidated);
}
}
mutable
是 AbstractUnsigned
的 private final boolean
。 number
是内部私有类之一,负责将Base
转换为 Shifted
,反之亦然。
与之前的“去年夏天我做了什么”相关的是两个内部对象:caster
和 shiftedConstructor
:final private Function<UnsignedNumber<N, Shifted>, Impl> caster;
final private Function<Shifted, Impl> shiftedConstructor;
这些是参数化函数,用于将N
(或Base
)转换为Shifted
,或者如果AbstractUnsigned<>
的当前实现实例是不可变的,则创建一个新的Impl
实例。
Shifted plus(Shifted value){
return spawnBelowZero.apply(summing.apply(shifted, value));
}
number
对象的添加方法。想法是始终在内部使用Shifted
,因为不确定何时会生成“原始”类型的正限制。shifted
是一个参数化字段,它承载着整个AbstractUnsigned<>
的值。另外两个Function<>
导出对象如下:final private BinaryOperator<Shifted> summing;
final private UnaryOperator<Shifted> spawnBelowZero;
前者执行两个Shifted
值的加法。而后者执行零以下转位的生成。
现在,来自工厂样板“hell”之一的示例,特别针对先前提到的spawnBelowZero
UnaryOperator<Shifted>
,针对AbstractUnsigned<Byte,Short>
:
...,
v-> v >= 0
? v
: (short) (Math.abs(Byte.MIN_VALUE) + Byte.MAX_VALUE + 2 + v),
...
0 1 2 3 ... 245 246 247 248 249 250 251 252 253 254 255 256
-8 -7 -6 -5 -4 -3 -2 -1
Number.class
子类。 AbstractUnsigned
本身就是Number.class
的子类,提供了所有方便的方法和常量,类似于其他“本地”Number.class
子类,包括MIN_VALUE
和MAX_VALUE
等等。例如,我编写了一个方便的可变子类方法makeDivisibileBy(Number n)
,它执行最简单的操作value - (value%n)
。如果您需要优化存储(例如大矩阵),可以使用负数编码更大的正数,以节省空间。然后,您需要移动数字值以在需要时获取实际值。例如,我只想处理短正数。以下是在Java中实现此操作的方法:
short n = 32767;
n = (short) (n + 10);
System.out.println(n);
int m = (int) (n>=0?n:n+65536);
System.out.println(m);
当一个短整数超出范围时,它会变成负数。但是,至少你可以将这个数字存储在16位中,并通过添加移位值(可以编码的不同值的数量)来恢复其正确的值。该值应在较大的类型(在我们的情况下为int)中恢复。这可能不是非常方便,但我发现在我的情况下是这样的。