关于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方法

 

设计的类图结构:

关于Java中使用容器的几个注意点_第1张图片

测试用例

对HashSet的测试

使用BasicType对HashSet进行测试

[java]  view plain copy
 
  1. /** 
  2.      * 使用{@link BasicType}对HashSet进行测试,对不同对象,equals为false,hashCode不相等<br> 
  3.      * 因此任一{@link BasicType}的对象在HashSet中都是唯一的,见测试{@link #testBasicTypeInHashSet} 
  4.      *  
  5.      * @see BasicType 
  6.      * @see test.HashTypeTest#testHashTypeInHashSet testHashTypeInHashSet 
  7.      */  
  8.     @Test  
  9.     public void testBasicTypeInHashSet() {  
  10.         Set<BasicType> set = new HashSet<BasicType>();  
  11.         set.add(new BasicType());  
  12.         set.add(new BasicType());  
  13.         set.add(new BasicType());  
  14.         set.add(new BasicType());  
  15.   
  16.         BasicType t = new BasicType();  
  17.         set.add(t);  
  18.         set.add(t);  
  19.   
  20.         // size为5,不是我们想要的  
  21.         Assert.assertEquals(set.size(), 5);  
  22.     }  

使用EqualsType对HashSet进行测试

[java]  view plain copy
 
  1. /** 
  2.      * 使用{@link EqualsType}对HashSet进行测试,对不同对象,equals可能为true,hashCode不相等<br> 
  3.      * 任一{@link EqualsType}的对象在HashSet中都是唯一的 
  4.      *  
  5.      * @see EqualsType 
  6.      * @see test.HashTypeTest#testHashTypeInHashSet testHashTypeInHashSet 
  7.      */  
  8.     @Test  
  9.     public void testEqualsTypeInHashSet() {  
  10.         Set<EqualsType> set = new HashSet<EqualsType>();  
  11.         set.add(new EqualsType());  
  12.         set.add(new EqualsType());  
  13.         set.add(new EqualsType());  
  14.         set.add(new EqualsType());  
  15.   
  16.         EqualsType t = new EqualsType();  
  17.         set.add(t);  
  18.         set.add(t);  
  19.   
  20.         // size为5,不是我们想要的  
  21.         Assert.assertEquals(set.size(), 5);  
  22.     }  


使用HashCodeType对HashSet进行测试

[java]  view plain copy
 
  1. /** 
  2.      * 使用{@link HashCodeType}对HashSet进行测试,对不同对象,equals可能为false,hashCode可能相等<br> 
  3.      * 任一{@link HashCodeType}的对象在HashSet中都是唯一的 
  4.      *  
  5.      * @see HashCodeType 
  6.      * @see test.HashTypeTest#testHashTypeInHashSet testHashTypeInHashSet 
  7.      */  
  8.     @Test  
  9.     public void testHashCodeTypeInHashSet() {  
  10.         Set<HashCodeType> set = new HashSet<HashCodeType>();  
  11.         set.add(new HashCodeType());  
  12.         set.add(new HashCodeType());  
  13.         set.add(new HashCodeType());  
  14.         set.add(new HashCodeType());  
  15.   
  16.         HashCodeType t = new HashCodeType();  
  17.         set.add(t);  
  18.         set.add(t);  
  19.         // size为5,不是我们想要的  
  20.         Assert.assertEquals(set.size(), 5);  
  21.     }  


使用HashType对HashSet进行测试

[java]  view plain copy
 
  1. /** 
  2.      * 使用{@link HashType}对HashSet进行测试,对不同对象,equals可能为true,hashCode可能相等<br> 
  3.      * 此时可以用HashSet去除重复对象(hashCode相等且是equals的),正是我们想要的 
  4.      *  
  5.      * @see HashType 
  6.      */  
  7.     @Test  
  8.     public void testHashTypeInHashSet() {  
  9.         Set<HashType> set = new HashSet<HashType>();  
  10.         set.add(new HashType());  
  11.         set.add(new HashType());  
  12.         set.add(new HashType());  
  13.         set.add(new HashType());  
  14.   
  15.         HashType t = new HashType();  
  16.         set.add(t);  
  17.         set.add(t);  
  18.         // 相等值对象被去除,size为1, 正是我们想要的  
  19.         Assert.assertEquals(set.size(), 1);  
  20.     }  


对TreeSet的测试

使用ComparableType对TreeSet进行测试

[java]  view plain copy
 
  1. /** 
  2.      * 使用ComparableType对TreeSet进行测试<br> 
  3.      * 使用TreeSet的类必须实现Comparable接口,但不必重写equals和hashCode方法,与TreeSet的内部实现有关 
  4.      *  
  5.      * @see tijava.container.type.ComparableType 
  6.      */  
  7.     @Test  
  8.     public void testComparableTypeInTreeSet() {  
  9.         Set<ComparableType> q = new TreeSet<ComparableType>();  
  10.         q.add(new ComparableType('d'3null));  
  11.         q.add(new ComparableType('d'4null));  
  12.         q.add(new ComparableType());  
  13.         q.add(new ComparableType());  
  14.         q.add(new ComparableType());  
  15.         q.add(new ComparableType());  
  16.         q.add(new ComparableType());  
  17.   
  18.         Assert.assertEquals(q.size(), 3);  
  19.         Assert.assertTrue(q.contains(new ComparableType('d'3null)));  
  20.   
  21.         q.remove(new ComparableType('d'3null));  
  22.         //remove ok  
  23.         Assert.assertEquals(q.size(), 2);  
  24.     }  


对PriorityQueue的测试

使用ComparableType对PriorityQueue进行测试

[java]  view plain copy
 
  1. /** 
  2.      * 使用ComparableType对PriorityQueue进行测试<br> 
  3.      * 在PriorityQueue中使用自定义类时,若只实现Comparable接口的类型,不支持查找和删除等操作 
  4.      *  
  5.      * @see test.EqualsComparableTypeTest#testEqualsComparableTypeInPriorityQueue 
  6.      *      testEqualsComparableTypeInPriorityQueue 
  7.      */  
  8.     @Test  
  9.     public void testComparableTypeInPriorityQueue() {  
  10.         Queue<ComparableType> q = new PriorityQueue<ComparableType>();  
  11.         q.add(new ComparableType('C'4"Empty trash"));  
  12.         q.add(new ComparableType('A'2"Feed dog"));  
  13.         q.add(new ComparableType('B'7"Feed bird"));  
  14.         q.add(new ComparableType('C'3"Mow lawn"));  
  15.         q.add(new ComparableType('A'1"Water lawn"));  
  16.         q.add(new ComparableType('B'1"Feed cat"));  
  17.   
  18.         Assert.assertEquals(q.size(), 6);  
  19.         Assert.assertFalse(q  
  20.                 .contains(new ComparableType('C'4"Empty trash")));  
  21.         Assert.assertFalse(q.remove(new ComparableType('C'4"Empty trash")));  
  22.           
  23.         //siz is still 6, not remove success  
  24.         Assert.assertEquals(q.size(), 6);  
  25.     }  


使用EqualsComparableType对PriorityQueue进行测试

[java]  view plain copy
 
  1. /** 
  2.      * 使用EqualsComparableType对PriorityQueue进行测试<br> 
  3.      * 在使用PriorityQueue是,在自定义类实现了equals方法后,就支持查找、删除等操作了 
  4.      *  
  5.      * @see test.ComparableTypeTest#testComparableTypeInPriorityQueue 
  6.      *      testComparableTypeInPriorityQueue 
  7.      */  
  8.     @Test  
  9.     public void testEqualsComparableTypeInPriorityQueue() {  
  10.         Queue<EqualsComparableType> q = new PriorityQueue<EqualsComparableType>();  
  11.         q.add(new EqualsComparableType('C'4"Empty trash"));  
  12.         q.add(new EqualsComparableType('A'2"Feed dog"));  
  13.         q.add(new EqualsComparableType('B'7"Feed bird"));  
  14.         q.add(new EqualsComparableType('C'3"Mow lawn"));  
  15.         q.add(new EqualsComparableType('A'1"Water lawn"));  
  16.         q.add(new EqualsComparableType('B'1"Feed cat"));  
  17.   
  18.         Assert.assertEquals(q.size(), 6);  
  19.         Assert.assertTrue(q.contains(new EqualsComparableType('C'4,  
  20.                 "Empty trash")));  
  21.         Assert.assertTrue(q.remove(new EqualsComparableType('C'4,  
  22.                 "Empty trash")));  
  23.           
  24.         // remove ok  
  25.         Assert.assertEquals(q.size(), 5);  
  26.     }  

你可能感兴趣的:(JAVA容器)