为什么Java API在int
甚至short
足够时使用byte
?
示例:班级DAY_OF_WEEK
中的Calendar
字段使用int
。
如果差异太小,那么为什么存在这些数据类型(short
,int
)?
答案 0 :(得分:162)
已经指出了一些原因。例如,"...(Almost) All operations on byte, short will promote these primitives to int"的事实。但是,明显的下一个问题是:为什么这些类型是否已提升为int
?
所以更深入一层:答案可能只与Java虚拟机指令集有关。正如Table in the Java Virtual Machine Specification中所总结的那样,所有整数算术运算(如添加,除法等)仅适用于int
类型和long
类型,以及不适用于较小的类型。
(旁白:较小的类型(byte
和short
)基本上只适用于数组。数组类似new byte[1000]
将占用1000个字节,像new int[1000]
这样的数组将占用4000个字节)
现在,当然,可以说“......显而易见的下一个问题是:为什么这些说明仅适用于int
(和{{1 }})?“。
上面提到的JVM规范中提到了一个原因:
如果每个类型化指令都支持所有Java虚拟机的运行时数据类型,那么将有更多的指令可以在一个字节中表示
此外,Java虚拟机可以被视为真实处理器的抽象。为较小的类型引入专用的Arithmetic Logic Unit是不值得的:它需要额外的晶体管,但它仍然只能在一个时钟周期内执行一次加法。 JVM设计时的主流架构是32位,恰好适用于32位long
。 (涉及64位int
值的操作是作为特殊情况实现的)。
(注意:最后一段有点过于简单,考虑到可能的矢量化等,但应该给出基本的想法,而不要过多地深入处理器设计主题)
long
存储为Calendar.DAY_OF_WEEK
来保存内存。但是在这里,Java类文件格式发挥作用:所有Fields in a Class File占用至少一个“槽”,其大小为一byte
(32位)。 (“宽”字段int
和double
占用两个广告位。因此明确地将字段声明为long
或short
也不会保存任何内存。
答案 1 :(得分:37)
(差不多)byte
,short
上的所有操作都会将它们提升为int
,例如,你不能写:
short x = 1;
short y = 2;
short z = x + y; //error
使用int
时,算术更简单直接,无需强制转换。
就空间而言,它与非常差别很小。 byte
和short
会使事情变得复杂,我认为这种微观优化并不值得,因为我们讨论的是固定数量的变量。
byte
是相关且有用的。这些原语也是有限的,如果计算可能会超出他们未来的限制怎么办?试着考虑Calendar
类的扩展,它可能会发展更大的数字。
另请注意,在64位处理器中,本地人员将保存在寄存器中,不会使用任何资源,因此使用int
,short
和其他原语不会产生任何影响所有。此外,许多Java实现对齐变量 * (和对象)。
另一方面,在数组中,byte
占用1个字节,short
占用2个字节,int
占用4个字节,因为在数组中只有开始,也可能是结束必须对齐。如果你想使用它会产生影响,例如byte
,那么你会真正注意到性能差异。
答案 2 :(得分:7)
因为与短路相比,使用整数时算术运算更容易。假设常量确实由short
值建模。然后你必须以这种方式使用API:
short month = Calendar.JUNE;
month = month + (short) 1; // is july
注意显式转换。在算术运算中使用时,短值会隐式提升为int
值。 (在操作数堆栈上,short甚至表示为int。)这使用起来非常麻烦,这就是int
值通常优于常量的原因。
与此相比,存储效率的提高是最小的,因为只存在固定数量的这种常数。我们谈论的是40个常数。将存储空间从int
更改为short
可以保护您40 * 16 bit = 80 byte
。有关详细信息,请参阅this answer。
答案 3 :(得分:5)
如果你使用了积分常量存储在它们所适合的最小类型中的哲学,那么Java会有一个严重的问题:每当程序员使用积分常量编写代码时,他们必须仔细注意他们的代码来检查是否常量的类型很重要,如果是这样,请查看文档中的类型和/或进行所需的任何类型的转换。
现在我们已经概述了一个严重的问题,您希望通过这种理念获得哪些好处?如果只有 运行时可观察到的那种变化的影响是你通过反射看到常量的那种类型,那我就不会感到惊讶了。 (当然,懒惰/不知情的程序员引入的错误不能正确地解释常量的类型)
权衡利弊是非常容易的:这是一个糟糕的哲学。
答案 4 :(得分:4)
虚拟机的设计复杂性取决于它可以执行多少种操作。对于32位整数,64位整数,32位浮点和64位浮点数这样的指令实现四次实现更容易,此外还要比以上是较小数值类型的版本。一个更有趣的设计问题是为什么应该有四种类型,而不是更少(使用64位整数执行所有整数计算和/或使用64位浮点值执行所有浮点计算)。使用32位整数的原因是,预计Java可以在许多平台上运行,其中32位类型可以像16位或8位类型一样快速地运行,但64位类型的操作会明显慢点。即使在16位类型可以更快使用的平台上,使用32位数量的额外成本也会被仅具有32位类型所提供的简单性所抵消。
对于在32位值上执行浮点计算,优点不太清楚。在某些平台上,通过将所有操作数转换为更高精度类型,添加它们,然后将结果转换回32位浮点数进行存储,可以最快速地执行float a=b+c+d;
之类的计算。在其他平台上,使用32位浮点值执行所有计算会更有效。 Java的创建者决定要求所有平台以相同的方式执行操作,并且他们应该支持32位浮点计算比更长的计算机更快的硬件平台,即使这种速度严重降低了PC的速度在典型的PC上,以及在没有浮点单元的许多机器上,浮点数学的精度和精度。注意,顺便说一句,取决于b,c和d的值,在计算上述float a=b+c+d;
之类的表达式时,使用更高精度的中间计算有时会产生比所有中间值更精确的结果操作数以float
精度计算,但有时会产生一个稍微不准确的值。无论如何,Sun决定一切都应该以同样的方式完成,并且他们选择使用最小精度float
值。
请注意,当大量数据类型以数组形式存储在一起时,较小数据类型的主要优点变得明显;即使单个变量的类型小于64位没有优势,也值得拥有可以更紧凑地存储更小值的数组;使局部变量为byte
而不是long
可以节省7个字节;具有1,000,000个数字的数组将每个数字保持为byte
而不是long
波7,000,000个字节。由于每个数组类型只需要支持一些操作(最明显的是读取一个项目,存储一个项目,复制数组中的一系列项目,或将一系列项目从一个数组复制到另一个数组),增加了更多的复杂性数组类型不像具有更多类型的直接可用离散数值的复杂性那么严重。
答案 5 :(得分:2)
实际上,有一个小优势。如果你有
class MyTimeAndDayOfWeek {
byte dayOfWeek;
byte hour;
byte minute;
byte second;
}
然后在典型的JVM上,它需要与包含单个int
的类一样多的空间。内存消耗量四舍五入到8或16字节的下一个倍数(IIRC,这是可配置的),因此存在实际保存的情况相当罕见。
如果相应的Calendar
方法返回byte
,则此类会更容易使用。但是没有这样的Calendar
方法,只有get(int)
因为其他字段而必须返回int
。较小类型的每个操作都会升级到int
,因此您需要进行大量投射。
最有可能的是,您要么放弃并切换到int
,要么写出像
void setDayOfWeek(int dayOfWeek) {
this.dayOfWeek = checkedCastToByte(dayOfWeek);
}
无论如何,DAY_OF_WEEK
的类型无关紧要。
答案 6 :(得分:2)
使用小于 CPU 总线大小的变量意味着需要更多周期。例如在更新内存中的单个字节时,64 位 CPU 需要读取整个 64 位字,只修改更改的部分,然后写回结果。
此外,当变量存储在寄存器中时,使用较小的数据类型需要开销,因为较小的数据类型的行为需要明确说明。由于无论如何都要使用整个寄存器,因此对于方法参数和局部变量使用较小的数据类型没有任何好处。
尽管如此,这些数据类型对于表示需要特定宽度的数据结构(例如网络数据包)或在大型数组中节省空间、牺牲速度可能很有用。