关于Java中使用容器的几个注意点

来源:互联网 发布:贵州网络微商传销 编辑:程序博客网 时间:2024/04/29 07:16

关于Java中使用容器的几个注意点

在看老代码时,看到一处使用HashSet的场景,检查了放入HashSet的类型参数,发现这个类型并没有重写equals和hashCode方法,这个后果的严重程度可想而知。就此暂时总结了以下几点,并配合测试代码,共勉!

总结点如下:

1.  使用HashSet/HahsMap时,定义的元素/key的类型必须同时重写equals和hashCode方法。

2.      TreeSet来说,只需实现Comparable接口,不需要重写equals和hashCode方法,至少java6是这样

3.      对其他容器(无hash),建议都重写equals方法,否则无法支持查找和删除操作。比如使用PriorityQueue时,若仅实现Comparable只支持对象的插入,只有在自定义类实现了equals方法后,才支持查找、删除等操作了。

其中,最重要的就是第一条,需谨记。若不确定对象会被如何使用,建议对任何自定义类型重写equals、hashCode和toString方法。

测试代码中示例类型的说明:

BasicType

  • 一个基础的类,包含三个字段,只重写了toString方法

ComparableType

  • 一个可比较的类,只实现了Comparable接口

EqualsType

  •  在BasicType的基础上重写了equals方法

HashCodeType

  • 在BasicType的基础上重写了hashCode方法

EqualsComparableType

  • 在ComparableType的基础上重写了equals方法

HashType

  • 在EqualsType基础上重写了hashCode方法

设计的类图结构:


测试用例

对HashSet的测试

使用BasicType对HashSet进行测试

/** * 使用{@link BasicType}对HashSet进行测试,对不同对象,equals为false,hashCode不相等<br> * 因此任一{@link BasicType}的对象在HashSet中都是唯一的,见测试{@link #testBasicTypeInHashSet} *  * @see BasicType * @see test.HashTypeTest#testHashTypeInHashSet testHashTypeInHashSet */@Testpublic void testBasicTypeInHashSet() {Set<BasicType> set = new HashSet<BasicType>();set.add(new BasicType());set.add(new BasicType());set.add(new BasicType());set.add(new BasicType());BasicType t = new BasicType();set.add(t);set.add(t);// size为5,不是我们想要的Assert.assertEquals(set.size(), 5);}

使用EqualsType对HashSet进行测试

/** * 使用{@link EqualsType}对HashSet进行测试,对不同对象,equals可能为true,hashCode不相等<br> * 任一{@link EqualsType}的对象在HashSet中都是唯一的 *  * @see EqualsType * @see test.HashTypeTest#testHashTypeInHashSet testHashTypeInHashSet */@Testpublic void testEqualsTypeInHashSet() {Set<EqualsType> set = new HashSet<EqualsType>();set.add(new EqualsType());set.add(new EqualsType());set.add(new EqualsType());set.add(new EqualsType());EqualsType t = new EqualsType();set.add(t);set.add(t);// size为5,不是我们想要的Assert.assertEquals(set.size(), 5);}


使用HashCodeType对HashSet进行测试

/** * 使用{@link HashCodeType}对HashSet进行测试,对不同对象,equals可能为false,hashCode可能相等<br> * 任一{@link HashCodeType}的对象在HashSet中都是唯一的 *  * @see HashCodeType * @see test.HashTypeTest#testHashTypeInHashSet testHashTypeInHashSet */@Testpublic void testHashCodeTypeInHashSet() {Set<HashCodeType> set = new HashSet<HashCodeType>();set.add(new HashCodeType());set.add(new HashCodeType());set.add(new HashCodeType());set.add(new HashCodeType());HashCodeType t = new HashCodeType();set.add(t);set.add(t);// size为5,不是我们想要的Assert.assertEquals(set.size(), 5);}


使用HashType对HashSet进行测试

/** * 使用{@link HashType}对HashSet进行测试,对不同对象,equals可能为true,hashCode可能相等<br> * 此时可以用HashSet去除重复对象(hashCode相等且是equals的),正是我们想要的 *  * @see HashType */@Testpublic void testHashTypeInHashSet() {Set<HashType> set = new HashSet<HashType>();set.add(new HashType());set.add(new HashType());set.add(new HashType());set.add(new HashType());HashType t = new HashType();set.add(t);set.add(t);// 相等值对象被去除,size为1, 正是我们想要的Assert.assertEquals(set.size(), 1);}


对TreeSet的测试

使用ComparableType对TreeSet进行测试

/** * 使用ComparableType对TreeSet进行测试<br> * 使用TreeSet的类必须实现Comparable接口,但不必重写equals和hashCode方法,与TreeSet的内部实现有关 *  * @see tijava.container.type.ComparableType */@Testpublic void testComparableTypeInTreeSet() {Set<ComparableType> q = new TreeSet<ComparableType>();q.add(new ComparableType('d', 3, null));q.add(new ComparableType('d', 4, null));q.add(new ComparableType());q.add(new ComparableType());q.add(new ComparableType());q.add(new ComparableType());q.add(new ComparableType());Assert.assertEquals(q.size(), 3);Assert.assertTrue(q.contains(new ComparableType('d', 3, null)));q.remove(new ComparableType('d', 3, null));        //remove okAssert.assertEquals(q.size(), 2);}


对PriorityQueue的测试

使用ComparableType对PriorityQueue进行测试

/** * 使用ComparableType对PriorityQueue进行测试<br> * 在PriorityQueue中使用自定义类时,若只实现Comparable接口的类型,不支持查找和删除等操作 *  * @see test.EqualsComparableTypeTest#testEqualsComparableTypeInPriorityQueue *      testEqualsComparableTypeInPriorityQueue */@Testpublic void testComparableTypeInPriorityQueue() {Queue<ComparableType> q = new PriorityQueue<ComparableType>();q.add(new ComparableType('C', 4, "Empty trash"));q.add(new ComparableType('A', 2, "Feed dog"));q.add(new ComparableType('B', 7, "Feed bird"));q.add(new ComparableType('C', 3, "Mow lawn"));q.add(new ComparableType('A', 1, "Water lawn"));q.add(new ComparableType('B', 1, "Feed cat"));Assert.assertEquals(q.size(), 6);Assert.assertFalse(q.contains(new ComparableType('C', 4, "Empty trash")));Assert.assertFalse(q.remove(new ComparableType('C', 4, "Empty trash")));//siz is still 6, not remove successAssert.assertEquals(q.size(), 6);}


使用EqualsComparableType对PriorityQueue进行测试

/** * 使用EqualsComparableType对PriorityQueue进行测试<br> * 在使用PriorityQueue是,在自定义类实现了equals方法后,就支持查找、删除等操作了 *  * @see test.ComparableTypeTest#testComparableTypeInPriorityQueue *      testComparableTypeInPriorityQueue */@Testpublic void testEqualsComparableTypeInPriorityQueue() {Queue<EqualsComparableType> q = new PriorityQueue<EqualsComparableType>();q.add(new EqualsComparableType('C', 4, "Empty trash"));q.add(new EqualsComparableType('A', 2, "Feed dog"));q.add(new EqualsComparableType('B', 7, "Feed bird"));q.add(new EqualsComparableType('C', 3, "Mow lawn"));q.add(new EqualsComparableType('A', 1, "Water lawn"));q.add(new EqualsComparableType('B', 1, "Feed cat"));Assert.assertEquals(q.size(), 6);Assert.assertTrue(q.contains(new EqualsComparableType('C', 4,"Empty trash")));Assert.assertTrue(q.remove(new EqualsComparableType('C', 4,"Empty trash")));// remove okAssert.assertEquals(q.size(), 5);}


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 惊!举重妖精被电竞奶狗缠上了 团宠小农女:带着功德空间去逃荒 被迫快穿后我只想当咸鱼 顶流影帝竹马来讨债 穿越后,太子妃靠种田轰动全京城 高考结束后我和死对头HE了 掉马后满级大佬被迫在热搜上开挂 农门悍妻:黑心莲夫君求抱抱 今日宜婚 嗜酒成瘾 肆意诱哄 快穿之反派女配是bug 落水后,我抢了未来首辅的婚! 玄学大佬在八零年代暴富了! 亚人娘补完手册 大女主爽文女配觉醒之后 太上长老她在线带娃 猎命人 夫人被迫觅王侯 穿书女配养崽忙 反派摄政王有了读心术,我躺赢了 星际大佬在荒野求生综艺爆红 满级女配在恋爱综艺当咸鱼 救命!战神王爷对我动了情 全球角色等级考核 大雍女提刑 相府逃妾登基了 电竞:荣光归你 撩疯!禁欲傅总嗜我成瘾 惊!废材她捶了天道 病娇战神的醋坛子翻了 当npc成为天降娘亲后她佛了 女主修仙,法力无边 惊!疯批狐王哭着求我摸尾巴 穿成农女后拥有了一整片森林 锦鲤系统:小奶包她欧气爆棚 不可退出 傲娇殿下整天粘着我要入赘 路人甲她又又又上位了 鬼医王妃,带着医毒空间回来复仇 凤落人间千般好