.toArray(새로운 MyClass[0]) 또는 .toArray(새로운 MyClass[myList.size()])?
어레이 리스트가 있는 경우
ArrayList<MyClass> myList;
어레이에 문의하고 싶은데 퍼포먼스상의 이유로
MyClass[] arr = myList.toArray(new MyClass[myList.size()]);
에 걸쳐서
MyClass[] arr = myList.toArray(new MyClass[0]);
?
두 번째 스타일은 좀 더 상세하지 않고 컴파일러가 빈 어레이를 작성하지 않도록 할 것이라고 생각했지만, 그것이 사실인지 궁금했습니다.
물론 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]);
}
}
블로그 포스트의 Arrays of the Wise of the Ancients에서 유사한 결과, 완전한 분석 및 토론을 볼 수 있습니다.요약하면 JVM 및 JIT 컴파일러에는 적절한 크기의 새로운 어레이를 저렴한 비용으로 만들고 초기화할 수 있는 몇 가지 최적화가 포함되어 있습니다.또한 어레이를 직접 작성하는 경우에는 최적화를 사용할 수 없습니다.
Java 5 의 Array List 에서는, 어레이의 사이즈가 적절한(또는 큰) 경우, 어레이는 이미 가득 찬 상태가 됩니다.결과적으로
MyClass[] arr = myList.toArray(new MyClass[myList.size()]);
는 하나의 배열 개체를 생성하여 채우고 "arr"로 되돌립니다.반면에
MyClass[] arr = myList.toArray(new MyClass[0]);
는 2개의 어레이를 작성합니다.두 번째는 길이가 0인 MyClass 배열입니다.즉, 오브젝트에 대한 오브젝트가 생성되어 즉시 폐기됩니다.소스 코드가 시사하는 한 컴파일러/JIT는 이 코드를 최적화할 수 없기 때문에 생성되지 않습니다.또한 길이 0 개체를 사용하면 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;
}
첫 번째 방법을 사용하여 오브젝트를 1개만 만들고 (암시적이지만 비용이 많이 드는) 캐스팅을 회피합니다.
JetBrains Intellij Idea 검사에서:
컬렉션을 배열로 변환하는 스타일에는 사전 크기 배열(c.toArray[c.size()] 등) 또는 빈 배열(c.toArray[0] 등)이 있습니다.
이전 Java 버전에서는 적절한 크기의 배열을 작성하기 위해 필요한 반사 호출이 매우 느렸기 때문에 사전 크기의 배열을 사용하는 것이 권장되었습니다.그러나 OpenJDK 6의 최신 업데이트 이후 이 콜이 내장되어 빈 어레이 버전의 퍼포먼스가 이전 크기 버전과 동일하고 경우에 따라서는 더 향상되었습니다.또, 사이즈가 미리 설정된 어레이를 건네주는 것은, 동시 또는 동기한 수집에서는 위험합니다.이는 사이즈가 있는 콜과 어레이 콜 사이에 데이터 레이스가 발생할 수 있기 때문에, 조작중에 수집이 동시에 축소되었을 경우 어레이의 마지막에 여분의 늘이 발생할 가능성이 있기 때문입니다.
이 검사에서는 빈 어레이(현대 Java에서 권장됨)를 사용하거나 사전 크기 어레이(이전 Java 버전 또는 비 HotSpot 기반 JVM에서 더 빠를 수 있음)를 사용하는 등 통일된 스타일을 따를 수 있습니다.
이 경우 최신 JVM은 반사형 어레이 구성을 최적화하므로 성능 차이는 거의 없습니다.그런 보일러 플레이트 코드로 컬렉션에 이름을 두 번 붙이는 것은 좋은 생각이 아니기 때문에 첫 번째 방법은 피하겠습니다.두 번째의 또 다른 장점은 동기화된 수집과 동시 수집을 함께 사용할 수 있다는 것입니다.최적화를 실시하는 경우는, 빈 어레이(빈 어레이는 불변으로 공유 가능)를 재이용하거나 프로파일러(!)를 사용합니다.
toArray는 전달된 배열이 적절한 크기(목록에 있는 요소에 맞도록 충분히 큰 크기)인지 확인하고, 맞으면 이를 사용합니다.따라서 어레이의 크기가 필요한 크기보다 작을 경우 새로운 어레이가 자동으로 생성됩니다.
이 경우 크기가 0인 배열은 불변하기 때문에 정적 최종변수로 안전하게 상승할 수 있습니다.그러면 코드가 좀 더 깨끗해져 호출할 때마다 어레이가 생성되지 않을 수 있습니다.어쨌든 새로운 어레이는 메서드 내에 작성되기 때문에 가독성 최적화입니다.
올바른 크기의 어레이를 전달하는 것이 빠른 버전이라고 할 수 있지만, 이 코드가 퍼포먼스의 병목 현상임을 증명할 수 없는 한 실행 시 퍼포먼스보다 읽기 쉬운 것을 권장합니다.
첫 번째 사례가 더 효율적입니다.
그 이유는 두 번째 경우:
MyClass[] arr = myList.toArray(new MyClass[0]);
런타임은 실제로 빈 어레이(사이즈 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가 경고 플래그를 지정하고 수정을 제안합니다.단일 키 입력으로 코드를 두 번째 유형에서 첫 번째 유형으로 변환합니다.
올바른 크기의 어레이에서 'toArray'를 사용하면 먼저 제로 크기의 어레이를 작성한 후 올바른 크기의 어레이를 작성하기 때문에 성능이 향상됩니다.하지만 당신이 말한 것처럼 그 차이는 무시할 수 있을 것 같습니다.
또한 javac 컴파일러는 최적화를 수행하지 않습니다.요즘은 모든 최적화가 런타임에 JIT/HotSpot 컴파일러에 의해 수행됩니다.어떤 JVM에서도 'toArray'에 대한 최적화를 알 수 없습니다.
따라서 질문에 대한 답변은 대부분 스타일의 문제이지만 일관성을 위해 (문서화되어 있든 없든) 모든 코딩 표준의 일부가 되어야 합니다.
integer의 샘플코드:
Integer[] arr = myList.toArray(new integer[0]);
언급URL : https://stackoverflow.com/questions/174093/toarraynew-myclass0-or-toarraynew-myclassmylist-size
'programing' 카테고리의 다른 글
Java에서 int를 char로 변환 (0) | 2022.08.09 |
---|---|
JTextField로의 값 변경 리스너 (0) | 2022.08.09 |
vue-router를 체크인합니다.각자가 루트에 대한 접근을 제한하지 않습니다. (0) | 2022.08.09 |
'for' 루프 내에서의 포스트 인크리먼트와 프리 인크리먼트에서도 동일한 출력이 생성됩니다. (0) | 2022.08.09 |
클래스가 최신 버전의 Java Environment로 컴파일되었습니다. (0) | 2022.08.09 |