Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java .toArray(新MyClass[0])或.toArray(新MyClass[myList.size()])?_Java_Performance_Coding Style - Fatal编程技术网

Java .toArray(新MyClass[0])或.toArray(新MyClass[myList.size()])?

Java .toArray(新MyClass[0])或.toArray(新MyClass[myList.size()])?,java,performance,coding-style,Java,Performance,Coding Style,假设我有一个ArrayList ArrayList<MyClass> myList; 结束 ? 我更喜欢第二种样式,因为它不太冗长,我假设编译器将确保不会真正创建空数组,但我一直在想这是不是真的 当然,在99%的情况下,这两者都没有区别,但是我想在我的普通代码和优化的内部循环之间保持一致的风格…使用“toArray”和正确大小的数组将执行得更好,因为替代方法将首先创建零大小的数组,然后创建正确大小的数组。然而,正如你所说,这种差异可能是可以忽略不计的 另外,请注意javac编译器不

假设我有一个ArrayList

ArrayList<MyClass> myList;
结束

?

我更喜欢第二种样式,因为它不太冗长,我假设编译器将确保不会真正创建空数组,但我一直在想这是不是真的


当然,在99%的情况下,这两者都没有区别,但是我想在我的普通代码和优化的内部循环之间保持一致的风格…

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

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


那么,您的问题的答案在很大程度上取决于风格,但出于一致性的考虑,应该成为您所遵循的任何编码标准的一部分(无论是否有文档记录)。

第一种情况更有效

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

MyClass[] arr = myList.toArray(new MyClass[0]);
运行时实际上创建了一个空数组(大小为零),然后在toArray方法内部创建另一个数组以适合实际数据。此创建使用反射完成,使用以下代码(取自jdk1.5.0_10):

public T[]toArray(T[]a){
如果(a.长度<尺寸)
a=(T[])java.lang.reflect.Array。
newInstance(a.getClass().getComponentType(),大小);
System.arraycopy(elementData,0,a,0,size);
如果(a.长度>尺寸)
a[size]=null;
返回a;
}

通过使用第一个表单,您可以避免创建第二个数组,也可以避免反射代码。

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

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

可以说,更快的版本是传递正确大小的数组,但除非您能证明此代码是性能瓶颈,否则在证明其他版本之前,您更喜欢可读性而不是运行时性能。

截至,如果数组大小正确(或更大),则数组将已填充。因此

MyClass[] arr = myList.toArray(new MyClass[myList.size()]);
将创建一个数组对象,填充它并将其返回到“arr”。另一方面

MyClass[] arr = myList.toArray(new MyClass[0]);
将创建两个数组。第二个是长度为0的MyClass数组。因此,有一个对象创建的对象将被立即扔掉。根据源代码的建议,编译器/JIT无法优化此代码,因此无法创建它。此外,使用零长度对象会导致在toArray()方法中进行强制转换

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

public T[]toArray(T[]a){
如果(a.长度<尺寸)
//创建a的运行时类型的新数组,但我的内容:
return(T[])array.copyOf(elementData,size,a.getClass());
System.arraycopy(elementData,0,a,0,size);
如果(a.长度>尺寸)
a[size]=null;
返回a;
}

使用第一种方法,只创建一个对象,避免(隐式但昂贵)铸件。

现代JVM在这种情况下优化反射阵列结构,因此性能差异很小。在这样的样板代码中为集合命名两次不是一个好主意,所以我会避免使用第一种方法。第二种方法的另一个优点是它可以处理同步和并发的集合。如果要进行优化,请重用空数组(空数组是不可变的,可以共享),或者使用探查器(!)。

整数的示例代码:

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

第二个版本的可读性稍差,但改进太少,不值得。第一种方法速度更快,在运行时没有缺点,所以这就是我使用的方法。但是我用第二种方式写,因为它打字更快。然后我的IDE将其标记为警告并提供修复。只需一次按键,即可将代码从第二种类型转换为第一种类型。

与直觉相反,Hotspot 8上最快的版本是:

MyClass[] arr = myList.toArray(new MyClass[0]);
我已经使用jmh运行了一个微基准测试。结果和代码如下,表明使用空数组的版本始终优于使用预大小数组的版本。请注意,如果可以重用大小正确的现有数组,结果可能会有所不同

基准测试结果(分数以微秒为单位,越小越好):


以下守则供参考:

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
public class SO29378922 {
  @Param({"1", "100", "1000", "5000", "10000", "100000"}) int n;
  private final List<Integer> list = new ArrayList<>();
  @Setup public void populateList() {
    for (int i = 0; i < n; i++) list.add(0);
  }
  @Benchmark public Integer[] preSize() {
    return list.toArray(new Integer[n]);
  }
  @Benchmark public Integer[] resize() {
    return list.toArray(new Integer[0]);
  }
}
@状态(Scope.Thread)
@基准模式(模式平均时间)
公共类SO29378922{
@参数({“1”、“100”、“1000”、“5000”、“10000”、“100000”)int n;
私有最终列表=新的ArrayList();
@设置公共void populateList(){
对于(inti=0;i

你可以在博客文章中找到类似的结果、完整的分析和讨论。总而言之:JVM和JIT编译器包含几种优化,使其能够以较低的成本创建和初始化新的大小正确的数组,如果您自己创建数组,则无法使用这些优化。

来自JetBrains Intellij Idea inspection:

将集合转换为数组有两种样式:使用 预先调整大小的数组(如c.toArray(新字符串[c.size()])或 使用空数组(如c.toArray(新字符串[0])。In 古老的
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;
}
Integer[] arr = myList.toArray(new integer[0]);
MyClass[] arr = myList.toArray(new MyClass[0]);
Benchmark                      (n)  Mode  Samples    Score   Error  Units
c.a.p.SO29378922.preSize         1  avgt       30    0.025 ▒ 0.001  us/op
c.a.p.SO29378922.preSize       100  avgt       30    0.155 ▒ 0.004  us/op
c.a.p.SO29378922.preSize      1000  avgt       30    1.512 ▒ 0.031  us/op
c.a.p.SO29378922.preSize      5000  avgt       30    6.884 ▒ 0.130  us/op
c.a.p.SO29378922.preSize     10000  avgt       30   13.147 ▒ 0.199  us/op
c.a.p.SO29378922.preSize    100000  avgt       30  159.977 ▒ 5.292  us/op
c.a.p.SO29378922.resize          1  avgt       30    0.019 ▒ 0.000  us/op
c.a.p.SO29378922.resize        100  avgt       30    0.133 ▒ 0.003  us/op
c.a.p.SO29378922.resize       1000  avgt       30    1.075 ▒ 0.022  us/op
c.a.p.SO29378922.resize       5000  avgt       30    5.318 ▒ 0.121  us/op
c.a.p.SO29378922.resize      10000  avgt       30   10.652 ▒ 0.227  us/op
c.a.p.SO29378922.resize     100000  avgt       30  139.692 ▒ 8.957  us/op
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
public class SO29378922 {
  @Param({"1", "100", "1000", "5000", "10000", "100000"}) int n;
  private final List<Integer> list = new ArrayList<>();
  @Setup public void populateList() {
    for (int i = 0; i < n; i++) list.add(0);
  }
  @Benchmark public Integer[] preSize() {
    return list.toArray(new Integer[n]);
  }
  @Benchmark public Integer[] resize() {
    return list.toArray(new Integer[0]);
  }
}