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


Answers

反直覺地說,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]);
  }
}
Question

假設我有一個ArrayList

ArrayList<MyClass> myList;

我想打電話給陣營,是否有性能的理由使用

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

過度

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

我更喜歡第二種風格,因為它不那麼冗長,我認為編譯器會確保空數組並不真正被創建,但我一直在想,如果這是真的。

當然,在99%的情況下,它不會以某種方式發揮作用,但我希望在我的普通代碼和優化的內部循環之間保持一致的樣式......




使用第一種情況,因為它更簡單並提供更簡潔的代碼。 原因是ToArray方法工作的底層方法是執行O(n)的複制操作。 不變的記憶不是什麼大不了的事情。 這些對象的管理非常有效,它會擴展到所需的大小。

在確定代碼中存在瓶頸之前,不要過多地進行優化。 如果你花費太多時間來優化這個,你只是在浪費時間。 我相信還有很多其他的東西可以優化,所以我會說使用其中一種。 如果你想要可讀性和較少的冗長性,那麼先拿一個。 如果您不介意額外的代碼並減少清晰度,請使用後者。




第二個是mor邊緣可讀,但沒有太大的改善,它不值得。 第一種方法更快,在運行時沒有缺點,所以這就是我使用的方法。 但是我把它寫成第二種方式,因為輸入速度更快。 然後我的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;
}

通過使用第一種形式,可以避免創建第二個數組並避免反射代碼。