文本简要概述了使用HashMap存储树型控件节点并尝试将其值集合直接转换为List时遇到的ClassCastException问题,解释了HashMap的values()方法返回的是Collection而非List,提供了正确转换List的方法,并探讨了HashMap的序列化机制及存储有序性问题,建议使用LinkedHashMap以保证存取顺序。
问题:
插件开发树型控件,初始化了一批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()源码。
我们发现values是new Values();构造的。
我们找到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是用来标识不需要序列化的属性。
那么问题来了,values是存储hashMap的所有值集合的属性,他如果不支持序列化,是不是我们hashMap的生命周期就在内存中,没办法持久化到磁盘?答案是否。肯定支持持久化,不过不是通过属性的持久化来完成,而是通过writeObject和readObject方法进行序列化与反序列化。具体的分析可以参考:https://blog.csdn.net/a3363642/article/details/102208460 。
思考:
回到苍穹开发中,我们的node是有序的,希望按照node1、node2这种顺序展示,存储的时候我们是按照顺序存储的,但是存储在hashMap中,会根据key来hash找对应的位置,最终是无序的,那么我们是否有其他方案来做到node存取有序?
答案是肯定的,我们可以使用有序的LinkedHashMap来存储数据,最终就是按照存储的顺序排序了。
如果发现文章有什么问题欢迎大家指出,我将积极验证修改。如果有帮到你,还请来一波三连:关注,点赞,收藏。觉得有用也可以分享到公司云之家群,惠及其他同事,感谢您的耐心观看~~~
推荐阅读