假设我有一个数组列表

ArrayList<MyClass> myList;

我想调用toArray,是否有性能方面的原因

MyClass[] arr = myList.toArray(new MyClass[myList.size()]);

over

MyClass[] arr = myList.toArray(new MyClass[0]);

?

我更喜欢第二种风格,因为它不那么冗长,而且我假设编译器将确保不会真正创建空数组,但我一直在想这是否属实。

当然,在99%的情况下,它不会以某种方式或其他方式产生区别,但我希望在我的普通代码和优化的内部循环之间保持一致的风格……


当前回答

toArray检查传递的数组大小是否正确(也就是说,大到足以容纳列表中的元素),如果是,则使用该数组。因此,如果数组的大小小于所需的大小,则会反射性地创建一个新数组。

在您的例子中,大小为0的数组是不可变的,因此可以安全地提升为静态final变量,这可能会使您的代码更简洁,从而避免在每次调用时创建数组。无论如何,方法内部都会创建一个新数组,因此这是一个可读性优化。

可以说,更快的版本是传递正确大小的数组,但除非您能证明此代码是性能瓶颈,否则优先考虑可读性而不是运行时性能,直到证明不是这样。

其他回答

使用'toArray'和正确大小的数组将执行得更好,因为替代方法将首先创建零大小的数组,然后创建正确大小的数组。然而,正如你所说,这种差异可能是可以忽略不计的。

另外,请注意javac编译器不执行任何优化。现在所有的优化都是由JIT/HotSpot编译器在运行时执行的。我不知道任何jvm中围绕'toArray'的任何优化。

那么,问题的答案很大程度上是一个风格问题,但为了一致性,应该成为您所遵循的任何编码标准的一部分(无论是有文档的还是其他的)。

第二个版本可读性稍好,但改进甚微,不值得这么做。第一种方法更快,在运行时没有缺点,所以我就用它。但我用第二种方式写,因为这样打字更快。然后我的IDE将其标记为警告并提供修复。只需击一次键,它就能将第二种类型的代码转换为第一种类型的代码。

第一种情况更有效。

这是因为在第二种情况下:

MyClass[] arr = myList.toArray(new MyClass[0]);

运行时实际上创建了一个空数组(大小为零),然后在toArray方法中创建另一个数组以适应实际数据。这个创建是使用下面的代码(来自jdk1.5.0_10)通过反射完成的:

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        a = (T[])java.lang.reflect.Array.
    newInstance(a.getClass().getComponentType(), size);
System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

通过使用第一种形式,可以避免创建第二个数组,也可以避免反射代码。

在这种情况下,现代jvm优化了反射数组结构,因此性能差异很小。在这样的样板代码中两次命名集合并不是一个好主意,因此我将避免使用第一个方法。第二种方法的另一个优点是它与同步和并发收集一起工作。如果您想进行优化,可以重用空数组(空数组是不可变的,可以共享),或者使用分析器(!)。

toArray检查传递的数组大小是否正确(也就是说,大到足以容纳列表中的元素),如果是,则使用该数组。因此,如果数组的大小小于所需的大小,则会反射性地创建一个新数组。

在您的例子中,大小为0的数组是不可变的,因此可以安全地提升为静态final变量,这可能会使您的代码更简洁,从而避免在每次调用时创建数组。无论如何,方法内部都会创建一个新数组,因此这是一个可读性优化。

可以说,更快的版本是传递正确大小的数组,但除非您能证明此代码是性能瓶颈,否则优先考虑可读性而不是运行时性能,直到证明不是这样。