为什么Java API使用int而不是short或byte?

为什么Java API使用int如果short或甚至byte就足够了?

示例: Calendar类中的DAY_OF_WEEK字段使用int

如果差异太小,为什么这些数据types(short,int)完全存在?

一些原因已经被指出。 例如, “…(几乎)所有对字节的操作,短的事实将这些原语提升为int” 。 然而,明显的下一个问题是: 为什么这些types被提升为int

所以要深入一层:答案可能只是与Java虚拟机指令集相关。 正如Java虚拟机规范中的表格所总结的那样, 所有的积分算术运算,比如加法,除法等等,只能用于inttypes和longtypes,而不能用于较小的types。

(另外:较小的types( byteshort )基本上只用于数组,new byte[1000]这样的数组需要1000字节,像new int[1000]这样的数组需要4000字节)

现在,当然可以这样说: “……明显的下一个问题是: 为什么这些指令只提供intlong )?

在上面提到的JVM规范中提到了一个原因:

如果每种types的指令都支持所有的Java虚拟机的运行时数据types,那么将会有比在一个字节中表示更多的指令

另外,Java虚拟机可以被认为是真实处理器的抽象。 引入专用的算术逻辑单元对于较小的types是不值得的:它将需要额外的晶体pipe,但它仍然只能在一个时钟周期内执行一次加法。 JVMdevise时的主要架构是32位,恰到好处的是32位int 。 (涉及64位long值的操作作为特例实现)。

(注意:考虑到可能的vector化等等,最后一段有些过于简化了,但是应该给出基本思想,而不要过分深入处理器devise主题)


编辑:一个简短的附录,侧重于从这个问题的例子,但在一个更普遍的意义上:人们也可以问,使用较小的types存储字段是否是有益的。 例如,有人可能认为可以通过将Calendar.DAY_OF_WEEK存储为byte来保存内存。 但在这里,Java类文件格式发挥作用: 类文件中的所有字段至less占用一个“槽”,其大小为一个int (32位)。 (“宽”字段, doublelong ,占用两个插槽)。 所以显式声明一个字段为short或者byte也不会保存任何内存。

(几乎)对byteshort所有操作将把它们提升为int ,例如,你不能写:

 short x = 1; short y = 2; short z = x + y; //error 

当使用int ,算术更简单直接,不需要投射。

在空间方面,这只是一个很小的差别。 byteshort会使事情复杂化,我不认为这个微型优化是值得的,因为我们正在谈论一个固定数量的variables。

当您为embedded式设备编程或处理文件/networking时, byte是相关且有用的。 而且这些原始数据是有限的,如果计算将来可能超出其极限呢? 尝试考虑可能演变大数字的Calendar类的扩展。

另外请注意,在64位处理器中,本地人将被保存在寄存器中,不会使用任何资源,所以使用intshort和其他原语根本不会有任何区别。 而且,许多Java实现alignmentvariables* (和对象)。


* byteshort如果是局部variables, variables甚至实例variables,则占用与int相同的空间。 为什么? 因为在(大多数)计算机系统中,variables地址是alignment的 ,所以例如,如果你使用单个字节,你实际上最终会得到两个字节 – 一个用于variables本身,另一个用于填充。

另一方面,在数组中, byte占用1个字节, short占用2个字节, int占用4个字节,因为在数组中只有开始和结束必须alignment。 如果你想使用System.arraycopy() ,这将会有所作为,那么你会注意到性能的差异。

因为与短裤相比,使用整数时算术运算更容易。 假定这些常数确实是由short值build模的。 那么你将不得不以这种方式使用API​​:

 short month = Calendar.JUNE; month = month + (short) 1; // is july 

注意显式的投射。 当在算术运算中使用short值时,隐式地将其提升为int值。 (在操作数堆栈中,短裤甚至表示为整数)。这将会非常麻烦,这就是为什么int值常常是常量的首选。

与此相比,存储效率的增益是最小的,因为只存在固定数量的这种常数。 我们正在谈论40个常量。 将它们的存储从int更改为short可以保证你的40 * 16 bit = 80 byte 。 看到这个答案进一步参考。

如果你使用哲学,其中整型常量以适合的最小types存储,那么Java将有一个严重的问题:每当程序员使用整型常量编写代码时,他们必须仔细注意他们的代码,以检查types常量很重要,如果是这样的话,查看文档中的types和/或做任何需要的types转换。

那么现在我们已经概述了一个严重的问题,那么您希望通过这个理念获得什么样的好处? 如果这种改变的唯一的运行时可观察的效果是通过reflection查看常量时得到的types,那么我不会感到惊讶。 (当然,无论懒惰/不知情的程序员引入了什么错误都不能正确地解释常量的types)

权衡利弊很容易:这是一个不好的理念。

虚拟机的devise复杂性是它可以执行多less种操作的函数。 有一个像“乘”的指令的四个实现比32位整数,64位整数,32位浮点和64位浮点更容易,到上面,更小的数字types的版本也是如此。 一个更有趣的devise问题是为什么应该有四种types,而不是更less(用64位整数执行所有整数计算和/或用64位浮点值执行所有浮点计算)。 使用32位整数的原因是,Java预计可以在许多平台上运行,其中32位types可以像16位或8位types一样快速地运行,但是对于64位types的操作将是显着的比较慢。 即使在16位types可以更快处理的平台上,使用32位数据的额外成本也会被只有 32位数据types所提供的简单性所抵消。

至于在32位值上执行浮点计算,其优点有点不太清楚。 有一些平台,像float a=b+c+d; 可以通过将所有操作数转换为更高精度的types,将它们相加,然后将结果转换回32位浮点数进行存储来最快速地执行。 在其他平台上,使用32位浮点值执行所有计算会更高效。 Java的创造者决定,所有的平台都应该被要求以同样的方式来做,而且他们应该偏好那些32位浮点计算速度比较长的计算速度更快的硬件平台,尽pipe这样会严重降低PC的速度以及典型PC上的浮点运算精度,以及许多没有浮点单元的机器。 注意,顺便说一下,根据b,c和d的值,在计算像上面提到的float a=b+c+d;这样的expression式时,使用更高精度的中间计算float a=b+c+d; 有时会得到比所有中间操作数在float精度下计算得到的结果明显更准确的结果,但是有时候会产生一个稍微不准确的值。 无论如何,Sun决定应该以同样的方式完成所有的事情,他们select使用最小精度float值。

请注意,较小数据types的主要优点在将大量数据一起存储在一个数组中时变得明显; 即使对于小于64位的types的单个variables没有好处,值得拥有能够更小型地存储更小值的数组; 有一个局部variables是一个byte而不是一个long保存七个字节; 拥有一个数字为1,000,000的数字将每个数字保存为一个byte而不是长波700万字节。 由于每个数组types只需要支持一些操作(最显着的是读取一个项目,存储一个项目,复制数组中的一系列项目,或者将一个项目的范围从一个数组复制到另一个),增加了更多的复杂性数组types不像具有更多types的可直接使用的离散数值那样复杂。

事实上,这会有一个小小的优势。 如果你有一个

 class MyTimeAndDayOfWeek { byte dayOfWeek; byte hour; byte minute; byte second; } 

那么在一个典型的JVM上,它需要和包含一个int的类一样多的空间。 内存消耗四舍五入到8或16字节的下一个倍数(IIRC,这是可configuration的),所以真正节省的情况是非常罕见的。

如果相应的Calendar方法返回一个byte这个类会稍微容易一些。 但是没有这样的Calendar方法,只有get(int)必须返回一个int因为其他字段。 对较小types的每个操作提升为int ,所以需要大量的投射。

最有可能的是,你要么放弃和切换到一个int或写入setters像

 void setDayOfWeek(int dayOfWeek) { this.dayOfWeek = checkedCastToByte(dayOfWeek); } 

那么DAY_OF_WEEK的types无关紧要。