HashSet<>を配列にするのは、どのやり方が速い?
HashSetを配列にする方法はいろいろあるかと思います。
toArrayを使う場合はこんな感じでしょうか。
toArray
hashSet -> hashSet.toArray(new Integer[hashSet.size()])
for文で実直に回す場合は、ちょっと長いですね。
enhancedFor
hashSet -> {
final Integer[] result = new Integer[hashSet.size()];
int i = 0;
for (Integer v : hashSet) {
result[i++] = v;
}
}
streamはこんなんでしょうか。
stream
hashSet -> hashSet.stream().toArray(size -> new Integer[size])
ここでは上記3つの方法を比較してみました。
ソース全体
要素10000個のHashSetを100万回配列にして、その平均時間を出力します。
com.example.HashSet2Array.java
package com.example;
import java.util.HashSet;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
*
* @author sengoku
*/
public class HashSet2Array {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
final int examCount = 1000000;
final int hasSize = 10000;
final Consumer<HashSet<Integer>> toArray
= hashSet -> hashSet.toArray(new Integer[hashSet.size()]);
final Consumer<HashSet<Integer>> enhancedFor = hashSet -> {
final Integer[] result = new Integer[hashSet.size()];
int i = 0;
for (Integer v : hashSet) {
result[i++] = v;
}
};
final Consumer<HashSet<Integer>> stream
= hashSet -> hashSet.stream().toArray(size -> new Integer[size]);
new HashSet2Array("toArray", examCount, hasSize, toArray);
new HashSet2Array("enhanced for", examCount, hasSize, enhancedFor);
new HashSet2Array("stream", examCount, hasSize, stream);
}
public HashSet2Array(
final String message,
final int examCount,
final int hashSize,
final Consumer<HashSet<Integer>> consumer) {
final HashSet<Integer> hashSet = createHashSet(hashSize);
final Double result = Stream
.iterate(1, x -> x + 1)
.limit(hashSize)
.collect(Collectors.averagingLong(x -> execAndMeasure(consumer, hashSet)));
System.out.println(message + " result: " + result);
}
private HashSet<Integer> createHashSet(final int size) {
final HashSet<Integer> hashSet = new HashSet<>();
IntStream.range(1, size).forEach(i -> hashSet.add(i));
return hashSet;
}
private long execAndMeasure(final Consumer<HashSet<Integer>> consumer, final HashSet<Integer> hashSet) {
final long start = System.nanoTime();
consumer.accept(hashSet);
return System.nanoTime() - start;
}
}
実行環境
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
実行結果
結果の数値は実行時間です。数値が小さいほど速いです。
toArray result: 168829.2181
enhanced for result: 114511.2577
stream result: 159047.3653
意外にtoArrayが遅いのは、結構複雑なことしてるせいですね。
↓
https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/AbstractCollection.java#L173
単純にfor文回すのが一番速くなるのも納得です。
考察
よっぽどシビアなパフォーマンス要件でない限り、一番読みやすいやり方を選んだほうがよさげです。