我有一些奇怪的行为。
[更新:给出完整的可运行示例:]
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编译的,那么在任何一种情况下使其工作的最有效的解决方法是什么?
答案 0 :(得分:3)
我认为我已经弄明白这里发生了什么,与final
关键字无关。如果您从此行中删除final
修饰符:
for(final Entry<SomeEnum, Integer> entry : map.entrySet()){
但是这是什么?来自the Map.Entry
JavaDocs(强调补充):
地图条目(键值对)。
Map.entrySet
方法返回地图的集合视图,其元素属于此类。 only 获取对映射条目的引用的方法来自此collection-view的迭代器。 这些Map.Entry
个对象仅在迭代期间有效。
...我读作&#34;不要尝试在Map.Entry
&#34;返回的集合视图上使用迭代之外的Map.entrySet
个对象。 - 这会调用未定义的行为。
This answer更详细地解释了它。 OP看到的问题与EnumMap
Map.Entry
的实施有关。
答案 1 :(得分:2)
对于EnumMap
和IdentityHashMap
,这是Java 6及更早版本中的行为。这是为了表现而做的(来源 - Josh Bloch,13:56,“Java Puzzlers”,2011年5月(Voo在评论中给出的原始视频链接))。它不再发生在Java 7中,现在您可以依赖于收集的Map.Entry对象,而不管后续迭代。
如果您打算在下一次迭代发生后使用条目,那么最好的解决方法是通过
克隆它new AbstractMap.SimpleImmutableEntry(entry)
并改为使用它。
答案 2 :(得分:1)
好的,我进入了源代码 - 当发送keySet,valueSet和entrySet时,EnumMap做了一件非常奇怪的事情。来自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){}
就像那样。
答案 3 :(得分: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());
}
}
}
我得到了这个输出。因此,第一次和第二次打印完全相同数量的条目。
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
答案 4 :(得分: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
看到这篇文章:Iterating over EnumMap#entrySet它会解释一些事情。
答案 5 :(得分:0)
根据Java Language Specification,当使用如下所示的迭代器时,增强的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
中变量的使用。