实体类当做 key
上面的示例中,我们用的是 String 类型当做 HashMap 中的 key。
这个场景能覆盖我们开发场景中的百分之 95 了。
但是偶尔会有那么几次,可能会把实体类当做 key 放到 HashMap 中去。
注意啊,面试题又来了:在 HashMap 中可以用实体类当对象吗?
那必须的是可以的啊。但是有坑,注意别踩进去了。
我拿前段时间看到的一个新闻给大家举个例子吧:
假设我要收集学生的家庭信息,用 HashMap 存起来。
那么我的 key 是学生对象, value 是学生家庭信息对象。
他们分别是这样的:
public class HomeInfo { private String homeAddr; private String carName; //省略改造方法和toString方法 } public class Student { private String name; private Integer age; //省略改造方法和toString方法 }
然后我们的测试用例如下:
public class HashMapTest { private static Map<Student, HomeInfo> hashMap = new HashMap<Student, HomeInfo>(); static { Student student = new Student("why", 7); HomeInfo homeInfo = new HomeInfo("大南街", "自行车"); hashMap.put(student, homeInfo); } public static void main(String[] args) { updateInfo("why", 7, "滨江路", "摩托"); for (Map.Entry<Student, HomeInfo> entry : hashMap.entrySet()) { System.out.println(entry.getKey()+"-"+entry.getValue()); } } private static void updateInfo(String name, Integer age, String homeAddr, String carName) { Student student = new Student(name, age); HomeInfo homeInfo = hashMap.get(student); if (homeInfo == null) { hashMap.put(student, new HomeInfo(homeAddr, carName)); } } }
初始状态下,HashMap 中已经有一个名叫 why 的 7 岁小朋友了,他家住大南街,家里的交通工具是自行车。
然后,有一天他告诉老师,他搬家了,搬到了滨江路去,而且家里的自行车换成了摩托车。
于是老师就通过页面,修改了 why 小朋友的家庭信息。
最后调用到了 updateInfo 方法。
嘿,你猜怎么着?
我带你看一下输出:
更新完了之后,他们班上出现了两个叫 why 的 7 岁小朋友了,一个住在大南街,一个住在滨江路。
更新变新增了,你说神奇不神奇?
现象出来了,那么根据现象定位问题代码不是手到擒来的事儿?
很明显,问题就出在这个地方:
这里取出来的 homeInfo 为空了,所以才会新放一个数据进去。
那么我们看看为啥这里为空。
跟着 hashMap.get() 源码进去瞅一眼:
标号为 ① 的地方是计算 key ,也就是 student 对象的 hashCode。而我们 student 对象并没有重写 hashCode,所以调用的是默认的 hashCode 方法。
这里的 student 是 new 出来的:
所以,这个 student 的 hashCode 势必和之前在 HashMap 里面的 student 不是一样的。
因此,标号为 ③ 的地方,经过 hash 计算后得出的 tab 数组下标,对应的位置为 null。不会进入 if 判断,这里返回为 null。
那么解决方案也就呼之欲出了:重写对象的 hashCode 方法即可。
是吗?
等等,你回来,别拿着半截就跑。我话还没说完呢。
接着看源码: