================ ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ================
前面已经介绍过Set集合,它类似与一个罐子,一旦把对象“丢进”Set集合中,集合里多个对象之间没有明显的顺序。Set集合和Collection基本上完全一样,没有提供任何额外的方法。实际上Set就是Collection,只是行为不同(Set不允许包含重复元素)。
Set集合不允许包含相同的元素,如果试图把两个相同的元素添加到同一个Set集合中时,添加操作失败,add方法返回false,则新元素不会被添加。
Set集合判断两个元素相同不是使用==运算符,而是根据equals方法。也就是说,如果两个对象用equals方法比较返回true,Set就不会接受这两个对象,反之则可以接受两个对象(甚至这两对象是同一个对象,Set也可把它们当成两个对象来处理,后面程序可以看到这种极端的情况)。如下是使用普通Set的示例程序代码:
1 package com.king.testcollection; 2 3 4 5 import java.util.HashSet; 6 7 import java.util.Set; 8 9 10 11 public class TestSet { 12 13 14 15 /** 16 17 * @author 王者黑桃 18 19 */ 20 21 public static void main(String[] args) { 22 23 // 定义一个Set集合 24 25 Set names=new HashSet(); 26 27 //添加一个字符串对象 28 29 names.add(new String("王者黑桃")); 30 31 //再次添加一个字符串对象 32 33 //因为两次添加的字符串对象通过equals方法比较相等,所以添加失败,返回false 34 35 boolean result=names.add(new String("王者黑桃")); 36 37 System.out.println(result); 38 39 //输出将看到集合中只有一个元素 40 41 System.out.println(names); 42 43 44 45 } 46 47 48 49 }
从上面程序中可以看出,names集合两次添加的字符串对象明显不是同一个对象(因为两次都调用了new关键字来创建字符串对象),这两个字符串对象通过==运算符判断肯定返回false,但它们通过equals方法判断返回true,所以添加失败。最后输出names集合将看到一个元素。
上面介绍的是Set集合的通用知识,因此完全适用与后面介绍的HashSet、TreeSet和EnumSet三个实现类,只是三个实现类还各有特色。
HashSet类
HashSet类是Set接口的典型实现,大多数时候使用Set集合时就是使用这三个实现类。HashSet按Hash的算法来存储集合中的元素,因此具有很好的存取和查找性能。
HashSet具有以下特点:
1.不能保证元素的排列顺序,顺序有可能发生变化。
2.HashSet不是同步的,如果多个线程同时访问一个Set集合,如果多个线程同时访问一个HashSet,如果有2个或2个以上线程同时修改了HashSet集合时,必须通过代码保证其同步。
3.集合元素值可以是NULL。
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来获取该对象的hashCode值,然后根据hashCode值来决定该对象在HashSet集合中的存储位置。如果两个元素通过equals方法比较返回true,但是它们的hashCode()方法返回值不相等,HashSet将会把它们存放在不同的位置,也就可以添加成功。
简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较想等,并且两个对象的hashCode()方法的返回值也相等。
下面程序分别提供了三个类,它们分别重写了equals、hashCode两个方法的一个或全部,通过此程序可以更清楚的了解HashSet集合判断两个元素想等的标准。
实例代码:
1 package com.king.testcollection; 2 3 4 5 import java.util.HashSet; 6 7 8 9 /** 10 11 * @author 王者黑桃 12 13 */ 14 15 //类A的equals方法总是返回true,但并没有重写其hashCode()方法 16 17 class A{ 18 19 public boolean equals(Object obj){ 20 21 return true; 22 23 } 24 25 } 26 27 //类B的hashCode()方法总是返回1,但并没有重写其equals方法 28 29 class B{ 30 31 public int hashCode(){ 32 33 return 1; 34 35 } 36 37 } 38 39 //类B的hashCode()方法总是返回2,equals方法的返回值true 40 41 class C{ 42 43 public int hashCode(){ 44 45 return 2; 46 47 } 48 49 public boolean equals(Object obj){ 50 51 return true; 52 53 } 54 55 } 56 57 public class TestHashSet { 58 59 60 61 62 63 public static void main(String[] args) { 64 65 HashSet names=new HashSet(); 66 67 // 分别向names集合中添加三个类的两个对象 68 69 names.add(new A()); 70 71 names.add(new A()); 72 73 names.add(new B()); 74 75 names.add(new B()); 76 77 names.add(new C()); 78 79 names.add(new C()); 80 81 System.out.println(names); 82 83 84 85 } 86 87 88 89 }
上面程序中names集合中分别添加了2个A对象、2个B对象和2个C对象,其中C类重写的equals()方法总是返回true,hashCode()总是返回2,这将导致HashSet将会把2个C对象当成同一个对象。运行程序将看到如下结果:
[com.king.testcollection.B@1, com.king.testcollection.B@1, com.king.testcollection.C@2, com.king.testcollection.A@6d4b473, com.king.testcollection.A@456d3d51]
这里有个问题需要注意:如果需要把一个对象放入HashSet集合中时,如果重写了equals()方法时,也应该重写其hashCode()方法,其规则是:如果两个对象通过equals()方法比较返回true时,这两个hashCode也应该相同。
如果两个对象通过quals()方法比较返回true,但这两个对象的hashCode()方法返回不同的hashCode时,这将导致HashSet会把这两个对象保存在HashSet的不同位置,从而这两个对象都将添加成功,这与Set集合的规则有点出入。
如果两个对象的hashCode()方法返回的hashCode相同,但它们通过equals()方法比较返回False时,这将更麻烦:因为两个对象的hashCode值相同,HashSet将试图把它们保存在同一位置,但实际上又不行(否则将只剩下一个对象),所以处理起来比较麻烦;而且HashSet访问集合元素时也是根据元素的hashCode值来访问的,如果HashSet中包含两个元素有相同的hashCode值,将导致性能下降。
HashSet中每个能存储元素的“槽位”通常别称为“桶”,如果有多个hashCode的值相同,但它们通过equals()方法比较返回False,就需要在一个“桶”里放多个元素,从而导致性能下降。
重写hashCode()方法的基本规则:
1.当两个对象通过equals()方法比较返回true时,这两个对象的hashCode也应该相等。
2.对象中用作equals比较标准的属性,都应该用来计算hashCode值。
HashSet还有一个子类LinkedHashSet,LinkedHashSet集合也是根据元素hashCode值来决定元素的存储位置,但他同时使用链表维护元素的次序,这样使的元素看起来是以插入的顺序保存的。也就是说当遍历LinkedHashSet集合里元素时,HashSet将会按元素的添加顺序来访问集合里的元素。
================ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ================ 详细请查看:http://edu.csdn.net