.toArray(new MyClass[0]) または .toArray(new MyClass[myList.size()])? 質問する

.toArray(new MyClass[0]) または .toArray(new MyClass[myList.size()])? 質問する

ArrayListがあると仮定します

ArrayList<MyClass> myList;

そしてtoArrayを呼び出したいのですが、パフォーマンス上の理由がありますか?

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

以上

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

?

私は 2 番目のスタイルの方が冗長性が少なく、コンパイラが空の配列が実際には作成されないことを確認するだろうと想定しているので、このスタイルを好みますが、それが本当かどうか疑問に思っています。

もちろん、99% のケースではどちらでも違いはありませんが、通常のコードと最適化された内部ループの間で一貫したスタイルを維持したいと考えています...

ベストアンサー1

直感に反しますが、Hotspot 8 上の最速バージョンは次のとおりです。

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

私は jmh を使用してマイクロ ベンチマークを実行しました。結果とコードは以下に示すとおりです。空の配列を使用したバージョンは、サイズがあらかじめ決められた配列を使用したバージョンよりも一貫して優れていることがわかります。正しいサイズの既存の配列を再利用できる場合は、結果が異なる可能性があることに注意してください。

ベンチマーク結果 (スコアはマイクロ秒単位、小さいほど良い):

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

同様の結果、完全な分析、および議論は、ブログ記事「Arrays of Wisdom of the Ancients」でご覧いただけます。要約すると、JVM および JIT コンパイラには、新しい正しいサイズの配列を低コストで作成して初期化できるようにするいくつかの最適化が含まれていますが、配列を自分で作成する場合、これらの最適化は使用できません。

おすすめ記事