hashMap.values()踩坑分析原创
金蝶云社区-JeremyG
JeremyG
4人赞赏了该文章 1,117次浏览 未经作者许可,禁止转载编辑于2021年12月20日 17:51:38

问题:

插件开发树型控件,初始化了一批node,放到hashMap中,便于后续重复使用。代码:

private Map<String, TreeNode> map = new HashMap<>(4);

{
    TreeNode node1 = new TreeNode("rootnode", "node1", "节点1");
    map.put("node1", node1);
    TreeNode node2 = new TreeNode("rootnode", "node2", "节点2");
    map.put("node2", node2);
    TreeNode node3 = new TreeNode("rootnode", "node3", "节点3");
    map.put("node3", node3);
    TreeNode node4 = new TreeNode("rootnode", "node4", "节点4");
    map.put("node4", node4);
}

然后在使用的时候,通过hashMap.values()方法把节点集合强转成list。

// 子节点初始化,会报错
List<TreeNode> nodeList = (List<TreeNode>) this.map.values();

结果报错:

java.lang.ClassCastException: java.util.HashMap$Values cannot be cast to java.util.List


分析:

我们看到报错信息,源类型是HashMap$Values,找到hashMap的values()源码。

image.png

我们发现values是new Values();构造的。

image.png

我们找到Values这个类,发现他是hashMap的内部类,继承了AbstractCollection<V>。AbstractCollection是实现Collection<E>接口。

所以hashMap.values()直接转List是会报错的,我们可以把values转成数组,再把数组转成list。或者通过ArrayList的构造方法,传入Collection来构造List。

// 子节点初始化,支持
Collection<TreeNode> values = this.map.values();
List<TreeNode> treeNodes = new ArrayList<>(values);


深入分析:

我们看HashMap的源码,发现values变量是在父类AbstractMap中定义的,这里有个关键字transient,我们了解,transient是用来标识不需要序列化的属性。

image.png

那么问题来了,values是存储hashMap的所有值集合的属性,他如果不支持序列化,是不是我们hashMap的生命周期就在内存中,没办法持久化到磁盘?答案是否。肯定支持持久化,不过不是通过属性的持久化来完成,而是通过writeObject和readObject方法进行序列化与反序列化。具体的分析可以参考:https://blog.csdn.net/a3363642/article/details/102208460 。


思考:

回到苍穹开发中,我们的node是有序的,希望按照node1、node2这种顺序展示,存储的时候我们是按照顺序存储的,但是存储在hashMap中,会根据key来hash找对应的位置,最终是无序的,那么我们是否有其他方案来做到node存取有序?

答案是肯定的,我们可以使用有序的LinkedHashMap来存储数据,最终就是按照存储的顺序排序了。



如果发现文章有什么问题欢迎大家指出,我将积极验证修改。如果有帮到你,还请来一波三连:关注,点赞,收藏。觉得有用也可以分享到公司云之家群,惠及其他同事,感谢您的耐心观看~~~

赞 4