===============《疯狂Java讲义精粹》读书笔记12 ------ Set集合========================
Set集合里多个对象之间没有明显的顺序,Set集合不允许包含相同的元素,如果add相同的元素就会返回false,新元素不会被加入。
import java.util.HashSet;
import java.util.Set;
/**
* Set里面不允许重复添加相同的对象
* @author <<疯狂Java讲义精粹>>
*
*/
public class TestSet {
public static void main(String[] args) {
Set name = new HashSet();
name.add("CocoonFan");
if(name.add(new String("CocoonFan"))){
System.out.println("添加成功!");
} else {
System.out.println("添加失败!\n当前的name的内容是:" + name);
}
}
}
输出的结果为:
添加失败!
当前的name的内容是:[CocoonFan]
很明显前后两次添加的字符串对象不是同一个对象,但是Set不会同时接受这两个对象。因为Set判断两个对象相同不是用==运算符,而是根据equals方法。
一、HashSet类
HashSet是Set接口的典型实现,HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。
它具有如下特点:
· 不能保证元素的排列顺序,顺序可能发生变化
· HashSet不是同步的
· 集合的元素值可以为null
· HashSet判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回的值也相等
下面的程序提供了A、B、C三个类分别重写了equals()、hashCode()两个方法或者全部:
import java.util.HashSet; /** * class A 重写了equals()方法始终返回true * */ class A{ public boolean equals(Object obj){ return true; } } /** * class B 重写了hashCode()方法,始终返回1. * */ class B{ public int hashCode() { return 1; } } /** * class C 重写了hashCode()方法(始终返回2),且重写了equals()方法 * */ class C { public int hashCode() { return 2; } public boolean equals(Object obj) { return true; } } public class TestEqualsHashCode { public static void main(String[] args) { HashSet name = new HashSet(); name.add(new A()); name.add(new A()); name.add(new B()); name.add(new B()); name.add(new C()); name.add(new C()); System.out.println(name); } }
输出结果:
[B@1, B@1, C@2, A@4f1d0d, A@18a992f]
从结果可以看出:
· HashSet 里面装的元素顺序是不确定的
· 集合里面只添加了两个A,两个B,一个C,说明Set判断两个对象的标准是equalse()方法和hashCode()方法都要一致
重写hashCode方法时应注意;
· 在程序运行中,同一个对象多次调用hashCode(0方法应该返回相同的值
· 当两个对象通过equals方法比较返回true 的时候,这两个对象的hashCode()的返回值也应该相等
二、LinkedHashSet类
HashSet还有一个子类LinkedHashSet,它也是根据元素的hashCode值来决定元素的储存位置,但是它同时还使用链表维护元素的秩序。它比较适合迭代访问元素。
输出元素的顺序和添加元素的顺序一致。
三、TreeSet类
TreeSet类是SortedSet几口的实现类,它可以确保元素处于排序状态,与HashSet相比TreeSet提供了几个额外的方法:
· Comparator comparator(): 如果TreeSet采用了定制排序,该方法返回定制排序所使用的Comparator;如果采用了自然排序则返回null
· Object last():返回集合中的最后一个元素
· Object first(): 返回集合中的第一个元素
· Object lower(Object e):返回集合中位于指定元素之前的元素(参考元素不需要是TreeSet中的元素)
· Object higher(Object e):返回集合中位于指定元素之后的元素(参考元素不需要是TreeSet中的元素)
· SortedSet subSet(fromElement, toElement):返回Set的子集合,范围 [fromElement, toElement)
· SortedSet headSet(toElement):返回Set的子集合,范围从开头到toElement
· SortedSet tailSet(fromElement):返回Set的子集合,由大于会等于fromElement的元素组成
下面是一个测试的例子
1 import java.util.TreeSet;
2
3
4 public class TestTreeSet {
5 public static void main(String[] args) {
6 TreeSet numbers = new TreeSet();
7 numbers.add(5);
8 numbers.add(2);
9 numbers.add(-9);
10 numbers.add(10);
11 numbers.add(-20);
12
13 //输出元素,将会看到集合已经被处于排序状态
14 System.out.println(numbers);
15
16 //输出第一个元素
17 System.out.println("集合的第一个元素是:" + numbers.first());
18
19 //输出集合里的最后一个元素
20 System.out.println("集合里的最后一个元素是: " + numbers.last());
21
22 //返回小于4(不包含) 的自子集,注意4并没有在集合中
23 System.out.println("小于4的自己是:" + numbers.headSet(4));
24
25 //返回大于或等于5的子集
26 System.out.println("大于等于5的子集是:" + numbers.tailSet(5));
27
28 //截取一个子集[A,B)
29 System.out.println("在[2,100)之间的子集是:" + numbers.subSet(2, 100));
30 }
31 }
输出的结果为:
[-20, -9, 2, 5, 10]
集合的第一个元素是:-20
集合里的最后一个元素是: 10
小于4的自己是:[-20, -9, 2]
大于等于5的子集是:[5, 10]
在[2,100)之间的子集是:[2, 5, 10]
TreeSet支持两种排序方法:自然排序和定制排序
(一)、自然排序
TreeSet会调用compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按照升序排列,这种方式就是自然排序。
Java提供了Comparable接口,当两个对象相比较的时候就会调用该接口的compareTo(Object obj)方法,相等返回0,大于返回一个正整数,小于返回一个负整数。
下面是实现了Comparable就扣的常用类:
· BigDecimal、BigInteger以及所有的整数值类型对应的包装类:按他们的数值大小进行比较
· Character: 按字符的UNICODE值进行比较
· Boolean:true对应的包装类实例大于false
· String :按字符集中的UNICODE值进行比较
· Date、Time:后面的时间、日期比前面的时间日期大
如果试图把一个对象添加到TreeSet中去,则该对象必须实现Comparable接口,否则程序将跑出异常。(只有第一个元素无需实现Comparable接口,但是取出的时候还是会跑出ClassCastException异常)。在实现compareTo(Object obj)方法时必须将比较的对象强制转换成相同类型。
当向TreeSet中添加多个不同的不同类型的对象的时候必须自己自己实现Comparable接口。例如先向TreeSet中添加一个字符串,再添加一个Data类型的元素的时候就会跑出异常。
如果两个对象通过equals()方法返回true的时候,这两个对象通过compareTo(Object obj)方法比较应该返回0,否则会很麻烦,因为compareTo(Object obj)比较相等时,TreeSet不会让地儿个元素加进去,这是就会与Set集合的规则(不能有重复的元素)相冲突。
有时候会有这样的问题,修改TreeSet中对象的成员变量之后可能与集合中的其他元素相等,这不久与Set集合的规则矛盾了吗?这时候该怎么处理呢?