Javaにおけるmapエルゴード方式の選択問題の詳細


1.述べる
JavaのMapの遍歴方式については、多くの文章では、keySetより効率が高いということが推奨されています。理由は:entrySet方法は一回に全てのkeyとvalueの集合を取ります。keySetはkeyの集合だけをもらって、各keyに対して、Mapの中で一回追加的にvalueを探しに行きます。それによって全体の効率が下がります。実際の状況はどうですか?
性能の実際のギャップを解消するために、key+value、遍歴key、valueなどの異なる場面の違いを含めて、いくつかの比較テストを行ってみました。
2.比較テスト
最初は簡単なテストしかしませんでしたが、結果としてkeySetの性能がいいということが分かりにくいです。さらに検証するために、異なる試験データを用いてより詳細な比較テストを行った。
2.1試験データ
2.1.1 HashMapテストデータ

HashMap-1,   100 ,key value  String,key   1、2、3……1000000:
Map<String, String> map = new HashMap<String, String>();
String key, value;
for (i = 1; i <= num; i++) {
  key = "" + i;
  value = "value";
  map.put(key, value);
}
HashMap-2はサイズが100万で、keyとvalueはStringで、keyの値は50、100、150、200、…、5000000:

Map<String, String> map = new HashMap<String, String>();
String key, value;
for (i = 1; i <= num; i++) {
  key = "" + (i * 50);
  value = "value";
  map.put(key, value);
}
2.1.2 TreeMapテストデータ
TreeMap-1はサイズが100万で、keyとvalueは全部Stringで、keyの値は1、2、3…100000:

Map<String, String> map = new TreeMap<String, String>();
String key, value;
for (i = 1; i <= num; i++) {
  key = "" + i;
  value = "value";
  map.put(key, value);
}
TreeMap-2はサイズが100万で、keyとvalueはStringで、keyの値は50、100、150、200、…、5000000で、より離散的です。

Map<String, String> map = new TreeMap<String, String>();
String key, value;
for (i = 1; i <= num; i++) {
  key = "" + (i * 50);
  value = "value";
  map.put(key, value);
}
2.2試験シーン
keySet、entrySet、valuesの様々な書き方を使って3つのシーンをテストします。key+valueを遍歴して、keyを遍歴して、valueのシーンを遍歴します。
2.2.1遍歴key+value
keySet遍歴key+value(書き方1):

Iterator<String> iter = map.keySet().iterator();
while (iter.hasNext()) {
  key = iter.next();
  value = map.get(key);
}
keySet遍歴key+value(書き方2):

for (String key : map.keySet()) {
  value = map.get(key);
}
entrySetはkey+valueを遍歴します(書き方1):

Iterator<Entry<String, String>> iter = map.entrySet().iterator();
Entry<String, String> entry;
while (iter.hasNext()) {
  entry = iter.next();
  key = entry.getKey();
  value = entry.getValue();
}
 entrySetはkey+valueを遍歴しています。

for (Entry<String, String> entry: map.entrySet()) {
  key = entry.getKey();
  value = entry.getValue();
}
2.2.2フィットネス
keySetはkeyを遍歴しています。

Iterator<String> iter = map.keySet().iterator();
while (iter.hasNext()) {
  key = iter.next();
}
keySetはkeyを遍歴しています。

for (String key : map.keySet()) {
}
 entrySetはkeyを遍歴します(書き方1):

Iterator<Entry<String, String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
  key = iter.next().getKey();
}
entrySetはkeyを遍歴します(書き方2):

for (Entry<String, String> entry: map.entrySet()) {
  key = entry.getKey();
}
2.2.3エルゴード
keySetはvalueを遍歴しています。

Iterator<String> iter = map.keySet().iterator();
while (iter.hasNext()) {
  value = map.get(iter.next());
}
keySetはvalueを遍歴しています。

for (String key : map.keySet()) {
  value = map.get(key);
}
entrySetはvalueを遍歴しています。

Iterator<Entry<String, String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
value = iter.next().getValue();
}
entrySetはvalueを遍歴しています。

for (Entry<String, String> entry: map.entrySet()) {
  value = entry.getValue();
}
values巡回value(書き方1):

Iterator<String> iter = map.values().iterator();
while (iter.hasNext()) {
value = iter.next();
}
values巡回value(書き方2):

for (String value : map.values()) {
}
2.3試験結果
2.3.1 HashMap試験結果
単位:ミリ秒
hashMap-1
hashMap-2
keySet遍歴key+value(書き方1)
39
93
keySet遍歴key+value(書き方2)
38。
87。
entrySet遍歴key+value(書き方1)
43
86。
entrySetはkey+valueを遍歴します(書き方2)
43
85。
単位:ミリ秒
hashMap-1
hashMap-2
keySetがkeyを遍歴する(書き方1)
27。
65。
keySetはkeyを遍歴する(書き方2)
26
64
entrySetはkeyを遍歴します(書き方1)
35
75
entrySetはkeyを遍歴します(書き方2)
34
74
単位:ミリ秒
hashMap-1
hashMap-2
keySetがvalueを巡回します(書き方1)
38。
87。
keySetはvalueを遍歴しています(書き方2)
37
87。
entrySetはvalueを遍歴します(書き方1)
34
61
entrySetはvalueを遍歴します(書き方2)
32
62
values巡回value(書き方1)
26
48
values巡回value(書き方2)
26
48
2.3.2 TreeMap試験結果
単位:ミリ秒
TreeMap-1
TreeMap-2
keySet遍歴key+value(書き方1)
430
451
keySet遍歴key+value(書き方2)
429
450
entrySet遍歴key+value(書き方1)
77
84
entrySetはkey+valueを遍歴します(書き方2)
70。
68
単位:ミリ秒
TreeMap-1
TreeMap-2
keySetがkeyを遍歴する(書き方1)
50
49
keySetはkeyを遍歴する(書き方2)
49
48
entrySetはkeyを遍歴します(書き方1)
66
64
entrySetはkeyを遍歴します(書き方2)
65。
63
単位:ミリ秒
TreeMap-1
TreeMap-2
keySetがvalueを巡回します(書き方1)
432
448
keySetはvalueを遍歴しています(書き方2)
430
448
entrySetはvalueを遍歴します(書き方1)
62
61
entrySetはvalueを遍歴します(書き方2)
62
61
values巡回value(書き方1)
46
46
values巡回value(書き方2)
45
46
3.結論
3.1 HashMapを使うなら
同時にkeyとvalueを遍歴する時、keySetとentrySet方法の性能の違いはkeyの具体的な状況によって決まります。複雑度(複雑なオブジェクト)、離散度、衝突率などです。つまり、HashMap検索valueのオーバヘッドに依存します。全てのkeyとvalueを一度に取り出す操作は性能オーバーヘッドがあり、この損失がHashMap検索valueのオーバーヘッドより小さい場合、entrySetの性能優位性が現れます。例えば、上記の比較テストでは、keyが最も単純な数値文字列である場合、keySetはむしろより効率的になり、10%の時間が経過する可能性がある。全体としては、やはりentrySetがオススメです。keyが簡単な時、その性能はkeySetよりやや低いかもしれませんが、コントロールできます。keyが複雑化するにつれて、entrySetのメリットは明らかに現れます。もちろん、私達は実際の状況によって選択できます。
keyを遍歴するだけで、keySet方法がより適切です。entrySetは無駄なvalueを取り出して、性能と空間を浪費しました。上記の試験結果では、keySetはentrySet法より23%少ない時間を消費しています。
valueを遍歴する時だけ、vlaues方法を使うのが一番いい選択です。entrySetはkeySet方法よりややいいです。
遍歴の異なる書き方では、以下のような書き方をおすすめします。その効率はやや高いです。

for (String key : map.keySet()) {
  value = map.get(key);
}
for (Entry<String, String> entry: map.entrySet()) {
  key = entry.getKey();
  value = entry.getValue();
}
for (String value : map.values()) {
}
3.2 TreeMapを使うなら
同時にkeyとvalueを遍歴する時、hashMapと違って、entrySetの性能はkeySetよりはるかに高いです。これはTreeMapの照会効率によって決定されたもので、つまりTreeMap検索valueのオーバーヘッドは大きく、entrySetが全てのkeyとvalueのオーバーヘッドを一括して取り出すことより明らかに高い。そのため、TreeMapを巡回した場合は、entrySetメソッドを強く推奨します。
keyを遍歴するだけで、keySet方法がより適切です。entrySetは無駄なvalueを取り出して、性能と空間を浪費しました。上記の試験結果では、keySetはentrySet方法より24%少ない。
valueを経るだけで、vlaues方法を使うのが一番いい選択です。entrySetもkeySet方法より明らかに優れています。
遍歴の異なる書き方では、以下のような書き方をおすすめします。その効率はやや高いです。

for (String key : map.keySet()) {
  value = map.get(key);
}
for (Entry<String, String> entry: map.entrySet()) {
  key = entry.getKey();
  value = entry.getValue();
}
for (String value : map.values()) {
}
締め括りをつける
以上がJavaのmapエルゴード方式の選択問題について詳しく説明した内容の全部です。興味のある友達はJavaにおけるmap内部記憶方式の解析を参照してください。 、  Javabeanとmapの相互変換方法コード例 、 話し相手とMapは互いに転化します。などです。問題があったらメッセージをください。編集者はすぐに皆さんに返事します。友達の私達のウェブサイトに対する支持に感謝します。