1. ホーム
  2. java

[解決済み】 .toArray(new MyClass[0]) or .toArray(new MyClass[myList.size()])?

2022-04-15 23:24:30

質問

ArrayListがあると仮定します。

ArrayList<MyClass> myList;

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

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

以上

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

?

私は2番目のスタイルの方が冗長でないので好きです。また、コンパイラは空の配列が本当に作成されないようにすると思っていましたが、そうなのでしょうか?

もちろん、99%の場合はどっちでもいいのですが、通常のコードと最適化された内部ループの間で、一貫したスタイルを保ちたいので...。

どのように解決するのですか?

逆算すると、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]);
  }
}


同様の結果、詳細な分析、考察はブログ記事でご覧いただけます。 古代の知恵の配列 . 要約すると、JVMとJITコンパイラは、新しい正しいサイズの配列を安価に作成し初期化するためのいくつかの最適化を含んでおり、自分で配列を作成する場合はそれらの最適化を使用することはできないのです。