关闭

[疯狂Java]集合:IdentityHashMap、EnumMap

标签: 疯狂Java集合IdentityHashMapEnumMap
326人阅读 评论(0) 收藏 举报
分类:

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一说;

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:300849次
    • 积分:5710
    • 等级:
    • 排名:第4707名
    • 原创:149篇
    • 转载:0篇
    • 译文:128篇
    • 评论:29条
    文章分类
    最新评论