通常编译器会生成执行装箱和拆箱的代码。但是,如果不需要盒装值,编译器会是什么? (Oracle标准)编译器是否足够智能以优化它?
看看这个方法:
public static void requireInRange(int index, Object[] array) {
if(index < 0 || index >= array.length)
throw new IndexOutOfBoundsException();
}
唯一相关的信息是array.length
,因此例如,将数组的每个值包装起来是没用的。就像在这段代码中一样:
int[] anArray = {3, 4, 2};
requireInRange(3, anArray);
编译器是否会实际插入用于装箱数组的每个值的代码?
答案 0 :(得分:39)
您的代码中没有自动装箱。事实上,鉴于:
public static void requireInRange(int index, Object[] array) {
...
}
int[] anArray = {3, 4, 2};
requireInRange(3, anArray); // DOES NOT COMPILE!!!
虽然int
可以自动装箱到Integer
,但int[]
NOT 会被Java自动装箱到Integer[]
。您可以编写库函数来执行此操作,但该语言不会促进此转换。
Arrays.asList(anIntArray)
被“破坏”,因为返回的内容实际上是一个元素List<Integer>
而不是返回List<int[]>
。
来自Java Language Guide/Autoboxing的引用:
将自动装箱和拆箱用于科学计算或其他对性能敏感的数字代码是不合适的。
Integer
不能代替int
; autoboxing和unboxing模糊了原始类型和引用类型之间的区别,但它们并没有消除它。
简而言之,每当自动装箱发生时,性能肯定会受到一点打击。某些事情有助于缓解这种情况,例如:这些类型中内置的缓存机制。这就是您获得以下内容的原因:
System.out.println(
((Integer) 0) == ((Integer) 0)
);
// true
System.out.println(
((Integer) 10000) == ((Integer) 10000)
);
// false (implementation-specific)
这里发生的事情是当0
自动框时,实际上没有创建新 Integer
实例:某个范围内的值为< em> cached 用于自动装箱,以帮助提高性能。大多数实现中的10000
可能超出此范围,但某些JVM实现允许您在必要时指定缓存范围。
有很多方法可以帮助requireInRange
使用任何类型的数组。不幸的是,使用Java的原语数组通常意味着很多重复。这意味着分别为int[]
,boolean[]
,byte[]
,Object[]
等提供重载。
更简洁的选择是使用反射,但这有其优点和缺点。一般来说,反射不应该是大多数情况下的首选解决方案。
话虽如此,java.lang.reflect.Array
确实有一个int getLength(Object array)
static
方法可以返回 ANY 数组的长度。它不是类型安全的(就像大多数反射机制一样);传递非数组编译,但在运行时抛出IllegalArgumentException
。
答案 1 :(得分:2)
编译器是否会实际插入代码 用于装箱阵列的每个值?
编译器将拒绝代码,因为无法将int[]
传递给采用Object[]
参数的方法。
自动装箱仅针对单个原始值发生,永远不会针对整个阵列。
答案 2 :(得分:2)
如果有疑问,您可以假设编译器不优化代码。通常它会对代码进行字面翻译。
此外,如果有疑问,您可以假设JVM在运行时优化代码方面做得很好。我不认为它有任何区别,除非你有充分的理由(比如分析器)怀疑它是一个问题。