1. IdentityHashMap:
1) 是一种特殊的HashMap,还是用key的hashCode来决定entry的槽位,但是不用key的equals方法来决定是否相等了,而是默认使用地址(即使实现了equals,equals也不起作用)来决定是否相等了!
2) 所以叫做Identity,就是直接用内存地址严格定义相等;
!!但是IdentityHashMap允许key和value都为null
3) 示例:
class R {
int val;
public R(int val) {
this.val = val;
}
@Override
public String toString() {
return "R[val:" + val + "]";
}
@Override
public boolean equals(Object obj) { // equals的标准写法
if (this == obj) { // 先比较地址
return true;
}
if (obj != null && obj.getClass() == R.class) { // 再比较类型(前提是obj不能为空)
R r = (R)obj; // 先把类型调整一致(当然可以直接return this.val == ((R)obj).val
// 但如果在return之前还要用obj进行一些其它复杂操作那用临时的类型转换太麻烦了
// 因此先协调类型才是最标准最合理的做法
return this.val == r.val;
}
return false; // 地址不同 || obj为空 || 类型不一致,那肯定不一样了!
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return this.val;
}
}
public class Test {
public static void main(String[] args) {
IdentityHashMap map = new IdentityHashMap<>();
map.put(new R(1), "fun");
map.put(new R(1), "kun");
R r = new R(3);
map.put(r, "xxx");
map.put(r, "yyy");
System.out.println(map); // {R[val:1]=fun, R[val:3]=yyy, R[val:1]=kun}
IdentityHashMap map2 = new IdentityHashMap<>();
map2.put(new String("lala"), "abc");
map2.put(new String("lala"), "xyz");
map2.put("mama", "777");
map2.put("mama", "888");
System.out.println(map2); // {mama=888, lala=xyz, lala=abc}
}
}
!!可以发现R的equals不起做用了,而String也实现了equals,同样也不起作用,但是两个"mama"的key没有重复,因为常量字符串是保存在内存常量池中的,因此地址相同;
4) IdentityHashMap的使用规矩:
i. 可以看到,它强行把地址的equals当equals方法用了!!
ii. 而我们之前说过,使用HashMap的原则是equals和hashCode要保持一致,因此我们使用IdentityHashMap的时候就应该让hashCode和地址相等保持一致!!
iii. 总结:用IdentityHashMap存放的元素最好不重写equals和hashCode(默认从Object继承来的都是直接比较地址,而hashCode返回的就是地址);
!!否则就会像上面的例子一样,equals相等并且hashCode也相等的情况下也出现了元素重复的现象!!这就非常混乱了!!
!!也就是说使用IdentityHashMap保存的元素最好是彻彻底底地根据地址判断是否相等!!!
2. EnumMap:
1) 和EnumMap类似,只不过要求key必须是枚举类型的值,并且key的枚举类型必须一致(所有元素的key必须都属于同一种枚举类型!),但value的类型随意(Object、String、自定义类型什么的随意);
2) 构造器:
i. 不像EnumSet构造时是通过各种静态工具方法构造,EnumMap必须用构造器构造;
ii. 这里只介绍最常用的版本:EnumMap(Class<K> keyType); // 用key的类型构造,例如:EnumMap map = new EnumMap(Season.class);
3) 和EnumSet不允许元素为null一样,EnumMap不允许key为null,但是value无所谓,因为key就代表了entry!但是判空、删除null的方法都能正常使用;
4) 接下来就可以把EnumMap当成普通Map使用了,可以使用所有Map里有的方法,示例:
enum Season {
SPRING, SUMMER
}
public class Test {
public static void main(String[] args) {
EnumMap map = new EnumMap<>(Season.class);
map.put(Season.SPRING, "lala");
map.put(Season.SUMMER, Integer.valueOf(11));
System.out.println(map);
}
}
5) 由于EnumSet、EnumMap都是使用二进制为向量进行映射的(EnumMap映射是通过key映射)的,因此不用担心重复问题,毕竟枚举类型也没有equals、hashCode一说;