4

私はいくつかの奇妙な行動をしています。

[更新:与えられた完全な実行可能な例:]

package finaltestwithenummapentry;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Map.Entry;

public class FinalTestWithEnumMapEntry {

    enum SomeEnum{
        ONE, TWO, THREE;
    }

    public static void main(String[] args) {
        EnumMap<SomeEnum, Integer> map = new EnumMap<SomeEnum, Integer>(SomeEnum.class);
        map.put(SomeEnum.ONE, 1);
        map.put(SomeEnum.TWO, 2);
        map.put(SomeEnum.THREE, 3);

        ArrayList<Entry<SomeEnum, Integer>> entryList = new ArrayList<Entry<SomeEnum, Integer>>();  

        for(final Entry<SomeEnum, Integer> entry : map.entrySet()){      
            System.out.println("Key is " + entry.getKey() + ", value is " + entry.getValue());     
            //This prints the correct keys and values      

            entryList.add(entry); 
        }  

        System.out.println("");

        for(Entry<SomeEnum, Integer> entry:entryList){     
            System.out.println("Key is " + entry.getKey() + ", value is " + entry.getValue());     
            //This prints only the last entry each time 
        }
    }
}

出力(JavaSE 1.6):

Key is ONE, value is 1
Key is TWO, value is 2
Key is THREE, value is 3

Key is THREE, value is 3
Key is THREE, value is 3
Key is THREE, value is 3

entryが最終的だと思っている私のは、毎回次のものによって上書きされているようです。for各アイテムをループ内の匿名の内部クラスインスタンスに渡すため、毎回正しいエントリをキャプチャできる必要があります。

[更新:この問題はJava 7には存在せず、Java 6(およびそれ以前)のみに存在します]

更新:Java 6または7に対してコンパイルされているかどうかに関係なく、コードを機能させる必要があるかもしれません。どちらの場合でも、コードを機能させるための最も効率的な回避策は何ですか?

4

6 に答える 6

3

私はここで何が起こっているのか理解したと思います、そしてそれはキーワードとは何の関係もありません。finalfinalこの行から修飾子を削除した場合:

for(final Entry<SomeEnum, Integer> entry : map.entrySet()){      

まったく同じ動作が発生します。

しかし、これは何ですか?JavaDocsから(強調が追加されました)Map.Entry

マップエントリ(キーと値のペア)。このMap.entrySetメソッドは、このクラスの要素を持つマップのコレクションビューを返します。マップエントリへの参照を取得する唯一の方法は、このコレクションビューのイテレータから取得することです。これらのMap.Entryオブジェクトは、反復期間中のみ有効です。

Map.Entry...私が読んだ「コレクションビューの反復以外でオブジェクトを使用しようとしないでくださいMap.entrySet」—これは未定義の動作を引き起こします。


この答えはそれをより詳細に説明しています。OPが見ている問題は、特にEnumMapの実装に関連していMap.Entryます。

于 2011-12-25T17:25:47.663 に答える
2

これは、Java6以前のEnumMapおよびの動作IdentityHashMapです。これはパフォーマンスのために行われました(出典-Josh Bloch、13:56、「JavaPuzzlers」、2011年5月(Vooがコメントで提供した元のビデオリンク))。Java 7以降では発生しなくなり、後続の反復に関係なく、収集されたMap.Entryオブジェクトに依存できるようになりました。

次の反復が発生した後にエントリを使用する場合の最善の回避策は、次の方法でエントリを複製することです。

new AbstractMap.SimpleImmutableEntry(entry)

代わりにそれを使用してください。

于 2011-12-25T23:48:53.950 に答える
1

OK、私はソースコードを調べました-EnumMapは、keySet、valueSet、entrySetの送信に関して非常に奇妙なことをします。JDKソースコードから:

/**
 * Since we don't use Entry objects, we use the Iterator itself as entry.
 */
private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>>
        implements Map.Entry<K,V>
{
    public Map.Entry<K,V> next() {
        if (!hasNext())
            throw new NoSuchElementException();
        lastReturnedIndex = index++;
        return this;
    }

EnumMapには、反復中に値を返す一連のカスタムクラスがあります。したがって、そのイテレータの外部で値を使用したくない理由です。

そのため、他のMap.Entryとは動作が異なります。解決策は、Entryオブジェクトを保持する代わりに、別のマップ/リストを保持/作成し、それをキー/値で埋めることです。

(以下の古い回答)

この場合の「final」は、単一の反復に対するその動作を定義します。内部クラスに渡す場合は、クラスのエントリポイントでfinalを定義します。

for(final Entry<K, V> entry : map.entrySet()){ 
  System.out.println("Key is " + entry.getKey() + ", value is " + entry.getValue());
  //This prints the correct keys and values
  doSomething(entry);
}

private void doSomething(final Entry<K,V> entry){}

そのように。

于 2011-12-25T15:46:05.793 に答える
1

これはどういう意味ですか?

//This prints only the last entry each time

このコードの使用(Java 7):

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

public class ForeachLoopWithFinal {

enum MyEnum {
    ONE("one"),
    TWO("two"),
    THREE("three");

    private String name;

    private MyEnum(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }

}

public static void main(String[] args) {
    List<Map.Entry<MyEnum, Integer>> entryList = new ArrayList<>();
    Map<MyEnum, Integer> map = new EnumMap<>(MyEnum.class);

    map.put(MyEnum.ONE, 1);
    map.put(MyEnum.TWO, 2);
    map.put(MyEnum.THREE, 3);

    for (final Map.Entry<MyEnum, Integer> entry : map.entrySet()) {
        System.out.printf("Key is %s, value is %s%n", entry.getKey(), entry.getValue());
        entryList.add(entry);
    }

    for (Map.Entry<MyEnum, Integer> entry : entryList) {
        System.out.printf("Key is %s, value is %s%n", entry.getKey(), entry.getValue());
    }
}

}

私はまさにこの出力を取得します。したがって、まったく同じ数のエントリが1回目と2回目に印刷されます。

Key is one, value is 1
Key is two, value is 2
Key is three, value is 3
Key is one, value is 1
Key is two, value is 2
Key is three, value is 3

あなたの例を使用して、私は同じようになります。

Key is ONE, value is 1
Key is TWO, value is 2
Key is THREE, value is 3

Key is ONE, value is 1
Key is TWO, value is 2
Key is THREE, value is 3
于 2011-12-25T16:10:13.233 に答える
0

ループが発生するたびに「エントリ」が作成されるため、実際には毎回新しい変数になると思います。ループ内の他の何かにエントリを設定しようとすると、コンパイラエラーが発生すると思います。

フィードバックに基づいて編集:あなたが私たちに見せていない何かがあるに違いありません。

Map<String, String> map = new HashMap<String, String>();
        map.put("barf", "turd");
        map.put("car", "bar");
        ArrayList<Entry<String, String>> entryList = new ArrayList<Entry<String, String>>();

        //Assuming I have a Map<K,V> called "map":
        for(final Entry<String, String> entry : map.entrySet()){ 
            System.out.println("Key is " + entry.getKey() + ", value is " + entry.getValue());
            //This prints the correct keys and values

            entryList.add(entry);
        }

        for(Entry<String,String> entry:entryList){
            System.out.println("Key is " + entry.getKey() + ", value is " + entry.getValue());
            //This prints only the last entry each time
        }

プリントアウト:

    Key is car, value is bar
    Key is barf, value is turd
    Key is car, value is bar
Key is barf, value is turd

この投稿を参照してください:EnumMap#entrySetの反復それは物事を説明します。

于 2011-12-25T15:44:37.853 に答える
0

Java言語仕様によると、for次のようなイテレータを使用する場合の拡張ループは次のとおりです。

for ( VariableModifiersopt Type Identifier: Expression) Statement

とまったく同じです:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
        VariableModifiersopt Type Identifier = #i.next();
   Statement
}

IのタイプはどこですかExpression.iterator())。あなたの場合、VariableModifiersoptですfinal。同等の形式から明らかなように、はfinal内の変数の使用にのみ適用されますStatement

于 2011-12-25T16:58:17.930 に答える