22

私はこのようなものを持っています:

Map<String, String> myMap = ...;

for(String key : myMap.keySet()) {
   System.out.println(key);
   System.out.println(myMap.get(key)); 
}

それで、 foreachループmyMap.keySet()で一度呼び出されますか?そうだと思いますが、ご意見をお聞かせください。

myMap.keySet()このようにforeachを使用すると( )がパフォーマンスに影響するのか、それともこれと同等であるのかを知りたいです。

Set<String> keySet = myMap.keySet();
for (String key : keySet) {
   ...
}
4

6 に答える 6

65

絶対に確実にしたい場合は、両方の方法でコンパイルし、逆コンパイルして比較します。私は次のソースでこれを行いました:

public void test() {
  Map<String, String> myMap = new HashMap<String, String>();

  for (String key : myMap.keySet()) {
    System.out.println(key);
    System.out.println(myMap.get(key));
  }

  Set<String> keySet = myMap.keySet();
  for (String key : keySet) {
    System.out.println(key);
    System.out.println(myMap.get(key));
  }
}

Jadを使用してクラスファイルを逆コンパイルすると、次のようになります。

public void test()
{
    Map myMap = new HashMap();
    String key;
    for(Iterator iterator = myMap.keySet().iterator(); iterator.hasNext(); System.out.println((String)myMap.get(key)))
    {
        key = (String)iterator.next();
        System.out.println(key);
    }

    Set keySet = myMap.keySet();
    String key;
    for(Iterator iterator1 = keySet.iterator(); iterator1.hasNext(); System.out.println((String)myMap.get(key)))
    {
        key = (String)iterator1.next();
        System.out.println(key);
    }
}

だからあなたの答えがあります。いずれかのforループ形式で1回呼び出されます。

于 2009-05-24T20:34:17.107 に答える
35

1 回だけ呼び出されます。実際、イテレータを使用してトリックを実行します。

さらに、あなたの場合、使用する必要があると思います

for (Map.Entry<String, String> entry : myMap.entrySet())
{
    System.out.println(entry.getKey());
    System.out.println(entry.getValue());
}

毎回マップ内を検索しないようにします。

于 2009-05-24T20:57:19.757 に答える
9

keySet()一度だけ呼び出されます。Iterable「拡張 for ループ」は、ループに使用される を取得するために使用するインターフェイスに基づいていIteratorます。Set個々の要素を取得できるインデックスや何かがないため、他の方法でa を反復することさえできません。

ただし、実際にすべきことは、この種のマイクロ最適化に関する懸念を完全に放棄することです。実際にパフォーマンスの問題が発生した場合、約 99% の確率で、それは自分では考えたこともなかったものです。

于 2009-05-24T20:38:25.303 に答える
7

答えはJava言語仕様にあり、逆コンパイルする必要はありません:)これは、拡張されたforステートメントについて読むことができるものです:

拡張forステートメントの形式は次のとおりです。

EnhancedForStatement:
        for ( VariableModifiersopt Type Identifier: Expression) Statement

式は型 Iterableであるか、配列型(§10.1)である必要があります。そうでない場合、コンパイル時エラーが発生します。

for拡張ステートメント(§14.14)のFormalParameter部分で宣言されたローカル変数のスコープは、含まれているステートメントです。

拡張ステートメントの意味はfor 、基本ステートメントへの翻訳によって与えられforます。

Expressionのタイプがのサブタイプである場合、式のタイプとIterableIます iterator()。拡張ステートメントは、次の形式forの基本ステートメントと同等です。for

for (I #i = Expression.iterator(); #i.hasNext(); ) {

        VariableModifiersopt Type Identifier = #i.next();
   Statement
}

#i拡張forステートメントが発生する時点でスコープ(§6.3)にある他の識別子(コンパイラー生成またはその他)とは異なるコンパイラー生成識別子はどこにありますか。

それ以外の場合、式には必ず配列型がありT[]ます。拡張ステートメントL1 ... Lm の直前の(おそらく空の)ラベルのシーケンスとしforます。次に、拡張forステートメントの意味は、次の基本for ステートメントによって与えられます。

T[] a = Expression;
L1: L2: ... Lm:
for (int i = 0; i < a.length; i++) {
        VariableModifiersopt Type Identifier = a[i];
        Statement
}

ここで、aおよびiは、拡張forステートメントが発生する時点でスコープ内にある他の識別子(コンパイラー生成またはその他)とは異なるコンパイラー生成の識別子です。

あなたの場合、myMap.keySet()のサブタイプを返すIterableので、拡張ステートメントは次の基本ステートメントforと同等です。for

for (Iterator<String> iterator = myMap.keySet().iterator(); iterator.hasNext();) {
   String key = iterator.next();

   System.out.println(key);
   System.out.println(myMap.get(key)); 
}

myMap.keySet()したがって、一度だけ呼び出されます。

于 2010-01-14T03:39:00.727 に答える
5

はい、どちらの方法でも1回だけ呼び出されます

于 2009-05-24T20:33:05.783 に答える
-3

ループエントリごとに1回だけ実行するように最適化されたコンパイラだと思います。

于 2009-05-24T20:35:03.453 に答える