《疯狂Java讲义精粹》读书笔记12 ------ Set集合(一)

===============《疯狂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集合的规则矛盾了吗?这时候该怎么处理呢?

 

posted @ 2013-03-11 15:26  Cocoon  阅读(287)  评论(0编辑  收藏  举报