假设我有一个数组列表

ArrayList<MyClass> myList;

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

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

over

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

?

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

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


当前回答

第一种情况更有效。

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

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;
}

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

其他回答

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

从Java 5中的ArrayList开始,如果数组的大小合适(或更大),它就会被填充。因此

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

将创建一个数组对象,填充它并返回给"arr"。另一方面

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

将创建两个数组。第二个是长度为0的MyClass数组。这里有一个对象创建用于一个马上会被丢弃的对象。就源代码所示,编译器/ JIT无法优化这个对象,因此不会创建它。此外,使用零长度对象会导致在toArray() -方法中进行强制转换。

请参阅ArrayList.toArray()的源代码:

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

使用第一种方法只创建一个对象,并避免(隐式但代价昂贵的)强制类型转换。

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

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

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

第一种情况更有效。

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

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