.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]);
?
두 번째 스타일은 덜 장황하기 때문에 선호합니다. 컴파일러가 빈 배열이 실제로 생성되지 않도록 할 것이라고 가정했지만 사실인지 궁금합니다.
물론 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 Wisdom of the Ancients 에서 유사한 결과, 전체 분석 및 토론을 찾을 수 있습니다 . 요약하자면, JVM 및 JIT 컴파일러에는 올바른 크기의 새 배열을 저렴하게 작성하고 초기화 할 수있는 여러 가지 최적화가 포함되어 있으며, 배열을 직접 작성하는 경우 이러한 최적화를 사용할 수 없습니다.
Java 5 의 ArrayList에서 , 배열의 크기가 적당하거나 더 큰 경우 배열이 이미 채워져 있습니다. 따라서
MyClass[] arr = myList.toArray(new MyClass[myList.size()]);
하나의 배열 객체를 생성하고 채우고 "arr"로 반환합니다. 반면에
MyClass[] arr = myList.toArray(new MyClass[0]);
두 개의 배열을 만듭니다. 두 번째는 길이가 0 인 MyClass의 배열입니다. 따라서 즉시 버려 질 오브젝트에 대한 오브젝트 작성이 있습니다. 소스 코드가 컴파일러 / JIT가 제안하지 않는 한 컴파일러 / 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;
}
첫 번째 방법을 사용하여 하나의 오브젝트 만 작성하고 내재적이지만 비용이 많이 드는 캐스팅을 피하십시오.
JetBrains Intellij Idea 검사에서 :
There are two styles to convert a collection to an array: either using a pre-sized array (like c.toArray(new String[c.size()])) or using an empty array (like c.toArray(new String[0]).
In older Java versions using pre-sized array was recommended, as the reflection call which is necessary to create an array of proper size was quite slow. However since late updates of OpenJDK 6 this call was intrinsified, making the performance of the empty array version the same and sometimes even better, compared to the pre-sized version. Also passing pre-sized array is dangerous for a concurrent or synchronized collection as a data race is possible between the size and toArray call which may result in extra nulls at the end of the array, if the collection was concurrently shrunk during the operation.
This inspection allows to follow the uniform style: either using an empty array (which is recommended in modern Java) or using a pre-sized array (which might be faster in older Java versions or non-HotSpot based JVMs).
Modern JVMs optimise reflective array construction in this case, so the performance difference is tiny. Naming the collection twice in such boilerplate code is not a great idea, so I'd avoid the first method. Another advantage of the second is that it works with synchronised and concurrent collections. If you want to make optimisation, reuse the empty array (empty arrays are immutable and can be shared), or use a profiler(!).
The first case is more efficient.
That is because in the second case:
MyClass[] arr = myList.toArray(new MyClass[0]);
the runtime actually creates an empty array (with zero size) and then inside the toArray method creates another array to fit the actual data. This creation is done using reflection using the following code (taken from 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;
}
By using the first form, you avoid the creation of a second array and also avoid the reflection code.
toArray checks that the array passed is of the right size (that is, large enough to fit the elements from your list) and if so, uses that. Consequently if the size of the array provided it smaller than required, a new array will be reflexively created.
In your case, an array of size zero, is immutable, so could safely be elevated to a static final variable, which might make your code a little cleaner, which avoids creating the array on each invocation. A new array will be created inside the method anyway, so it's a readability optimisation.
Arguably the faster version is to pass the array of a correct size, but unless you can prove this code is a performance bottleneck, prefer readability to runtime performance until proven otherwise.
Using 'toArray' with the array of the correct size will perform better as the alternative will create first the zero sized array then the array of the correct size. However, as you say the difference is likely to be negligible.
Also, note that the javac compiler does not perform any optimization. These days all optimizations are performed by the JIT/HotSpot compilers at runtime. I am not aware of any optimizations around 'toArray' in any JVMs.
The answer to your question, then, is largely a matter of style but for consistency's sake should form part of any coding standards you adhere to (whether documented or otherwise).
The second one is marginally mor readable, but there so little improvement that it's not worth it. The first method is faster, with no disadvantages at runtime, so that's what I use. But I write it the second way, because it's faster to type. Then my IDE flags it as a warning and offers to fix it. With a single keystroke, it converts the code from the second type to the first one.
sample code for integer :
Integer[] arr = myList.toArray(new integer[0]);
참고URL : https://stackoverflow.com/questions/174093/toarraynew-myclass0-or-toarraynew-myclassmylist-size
'development' 카테고리의 다른 글
ARC 가능 코드에서 "이 블록에서 [오브젝트]를 강력하게 캡처하면 유지주기가 발생할 수 있습니다." (0) | 2020.06.21 |
---|---|
SQL Server에서 상수 1 또는 0으로 비트를 암시 (0) | 2020.06.21 |
java.exe와 javaw.exe의 차이점 (0) | 2020.06.21 |
약어의 의미 std :: string (0) | 2020.06.21 |
반환 값 또는 매개 변수 중 어느 것이 더 낫습니까? (0) | 2020.06.21 |