迭代通过EnumMap#entrySet [英] Iterating over EnumMap#entrySet

查看:134
本文介绍了迭代通过EnumMap#entrySet的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

枚举 Map#entrySet 无法像所有Map实现一样正常工作,特别是对于EnumMap, IdentityHashMap 这里是来自Josh Bloch的拼音演示文稿的示例代码(Puzzle 5) -

  public class Size {

私人枚举性{MALE ,FEMALE}

public static void main(String [] args){
printSize(new HashMap< Sex,Sex>());
printSize(new EnumMap< Sex,Sex>(Sex.class));
}

private static void printSize(Map< Sex,Sex> map){
map.put(Sex.MALE,Sex.FEMALE);
map.put(Sex.FEMALE,Sex.MALE);
map.put(Sex.MALE,Sex.MALE);
map.put(Sex.FEMALE,Sex.FEMALE);
Set< Map.Entry< Sex,Sex>> set =
new HashSet< Map.Entry< Sex,Sex>>(map.entrySet());
System.out.println(set.size());
}
}

并且是产生错误的结果 -



应为

  2 
2



但产生

  2 
1

但如果我尝试使用下面的代码 - result



UPDATE

虽然结果集的大小为2,但条目相同。

  public class Test {

私人列举性别{MALE,FEMALE}

public static void main(String ... args){
printSize(new HashMap< Sex,String>());
printSize(new EnumMap< Sex,String>(Sex.class));
}


private static void printSize(Map< Sex,String> map){
map.put(Sex.MALE,1);
map.put(Sex.FEMALE,2);
map.put(Sex.MALE,3);
map.put(Sex.FEMALE,4);
Set< Map.Entry< Sex,String>> set =
new HashSet< Map.Entry< Sex,String>>(map.entrySet());
System.out.println(set.size());
}
}



我甚至尝试了上述代码与两个不同的枚举类型作为键和值。



这似乎是问题只有当EnumMap有一个相同的枚举作为键和值。



我想知道为什么是这样的?或者我缺少某些东西,但是当ConcurrentHashMap固定回来的时候不是固定的?

解决方案

c $ c> EnumMap.EntryIterator.next()实现。这应该足以找出问题。



一个线索是,结果集是:

  [FEMALE = 2,FEMALE = 2] 

正确的结果。



您看到的效果是由于 EnumMap.EntryIterator.hashCode()实现这里Map.Entry)。

  h = key ^ value 

这会导致

产生的条目具有相同的哈希值。

  map.put .MALE,Sex.MALE); 
map.put(Sex.FEMALE,Sex.FEMALE);

a stable 0。



/ p>

  map.put(Sex.MALE,Sex.FEMALE); 
map.put(Sex.FEMALE,Sex.MALE);

这里是一个不稳定的如果键和值的哈希值是相同的值,你总会看到效果,因为: a ^ b == b ^ a 。这将导致条目的散列值相同。



如果条目具有相同的散列值,那么它们最终会在散列表的同一个桶中,而equals将总是起作用因为它们是相同的对象。



有了这个知识,我们现在可以产生与其他类型像Integer(我们知道hashCode实现)相同的效果: p>

  map.put(Sex.MALE,Integer.valueOf(Sex.MALE.hashCode())); 
map.put(Sex.FEMALE,Integer.valueOf(Sex.MALE.hashCode()));

[FEMALE = 1671711,FEMALE = 1671711]

奖金:EnumMap实现打破equals()契约:

  EnumMap< Sex,Object& enumMap = new EnumMap< Sex,Object>(Sex.class); 
enumMap.put(Sex.MALE,1);
enumMap.entrySet()。iterator()。next()。equals(enumMap.entrySet()。iterator());

抛出:

 code>线程main中的异常java.lang.IllegalStateException:删除条目
在java.util.EnumMap $ EntryIterator.checkLastReturnedIndexForEntryUse(EnumMap.java:601)
在java.util。 EnumMap $ EntryIterator.getValue(EnumMap.java:557)
在java.util.EnumMap $ EntryIterator.equals(EnumMap.java:576)
at com.Test.main(Test.java:13)


Enumerating over Map#entrySet doesn't work as expected for all Map implementations, specially for EnumMap, IdentityHashMap and here is the sample code from Josh Bloch's puzzler presentation (Puzzle 5) -

public class Size {

    private enum Sex { MALE, FEMALE }

    public static void main(String[] args) { 
        printSize(new HashMap<Sex, Sex>()); 
        printSize(new EnumMap<Sex, Sex>(Sex.class)); 
    }

    private static void printSize(Map<Sex, Sex> map) { 
        map.put(Sex.MALE,   Sex.FEMALE); 
        map.put(Sex.FEMALE, Sex.MALE); 
        map.put(Sex.MALE,   Sex.MALE); 
        map.put(Sex.FEMALE, Sex.FEMALE); 
        Set<Map.Entry<Sex, Sex>> set = 
            new HashSet<Map.Entry<Sex, Sex>>(map.entrySet()); 
        System.out.println(set.size()); 
    }
}

and yes that produces the wrong result -

supposed to be

 2 
 2

but produces

2 
1

but if I try with below code - it produces the correct result

UPDATE
Though the size of the resulting Set is 2 but Entries are same.

public class Test{

 private enum Sex { MALE, FEMALE } 

    public static void main(String... args){
        printSize(new HashMap<Sex, String>());
        printSize(new EnumMap<Sex, String>(Sex.class));
    }


    private static void printSize(Map<Sex, String> map) {
        map.put(Sex.MALE,   "1");
        map.put(Sex.FEMALE, "2");
        map.put(Sex.MALE,   "3");
        map.put(Sex.FEMALE, "4");
        Set<Map.Entry<Sex, String>> set =
            new HashSet<Map.Entry<Sex, String>>(map.entrySet());
        System.out.println(set.size());
    }
}

I even tried the above code with the two different enum types as key and value.

This seems to be issue only if EnumMap has a same enum as a key and value.

I would like to know why is this? or I'm missing something.why it's not fixed when ConcurrentHashMap got fixed long back?

解决方案

Have a look at the EnumMap.EntryIterator.next() implementation. This should be enough to figure out the problem.

A clue is that the resulting set is:

[FEMALE=2, FEMALE=2]

which is not the correct result.

The effect you see is due to the EnumMap.EntryIterator.hashCode() implementation (which is the Map.Entry here). It's

h = key ^ value

This results in the same hash value for the entries produced by

map.put(Sex.MALE,   Sex.MALE); 
map.put(Sex.FEMALE, Sex.FEMALE); 

a stable 0.

or

map.put(Sex.MALE,   Sex.FEMALE); 
map.put(Sex.FEMALE, Sex.MALE);

here it's an instable (for multiple executions) int value. You will always see the effect if key and value hashs are the same value because: a ^ b == b ^ a. This results in the same hash value for the Entry.

If entries have the same hash value they end up in the same bucket of the hash table and the equals will always work as they are the same object anyway.

With this knowledge we can now also produce the same effect with other types like Integer (where we know the hashCode implementation):

map.put(Sex.MALE,   Integer.valueOf(Sex.MALE.hashCode())); 
map.put(Sex.FEMALE, Integer.valueOf(Sex.MALE.hashCode()));

[FEMALE=1671711, FEMALE=1671711]

Bonus: The EnumMap implementation breaks the equals() contract:

EnumMap<Sex, Object> enumMap = new EnumMap<Sex, Object>(Sex.class);
enumMap.put(Sex.MALE, "1");
enumMap.entrySet().iterator().next().equals(enumMap.entrySet().iterator());

Throws:

Exception in thread "main" java.lang.IllegalStateException: Entry was removed
    at java.util.EnumMap$EntryIterator.checkLastReturnedIndexForEntryUse(EnumMap.java:601)
    at java.util.EnumMap$EntryIterator.getValue(EnumMap.java:557)
    at java.util.EnumMap$EntryIterator.equals(EnumMap.java:576)
    at com.Test.main(Test.java:13)

这篇关于迭代通过EnumMap#entrySet的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆