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

关于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
	 */
	@Test
	public 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
	 */
	@Test
	public 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
	 */
	@Test
	public 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
	 */
	@Test
	public 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
	 */
	@Test
	public 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 ok
		Assert.assertEquals(q.size(), 2);
	}


对PriorityQueue的测试

使用ComparableType对PriorityQueue进行测试

/**
	 * 使用ComparableType对PriorityQueue进行测试<br>
	 * 在PriorityQueue中使用自定义类时,若只实现Comparable接口的类型,不支持查找和删除等操作
	 * 
	 * @see test.EqualsComparableTypeTest#testEqualsComparableTypeInPriorityQueue
	 *      testEqualsComparableTypeInPriorityQueue
	 */
	@Test
	public 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 success
		Assert.assertEquals(q.size(), 6);
	}


使用EqualsComparableType对PriorityQueue进行测试

/**
	 * 使用EqualsComparableType对PriorityQueue进行测试<br>
	 * 在使用PriorityQueue是,在自定义类实现了equals方法后,就支持查找、删除等操作了
	 * 
	 * @see test.ComparableTypeTest#testComparableTypeInPriorityQueue
	 *      testComparableTypeInPriorityQueue
	 */
	@Test
	public 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 ok
		Assert.assertEquals(q.size(), 5);
	}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值