zoukankan      html  css  js  c++  java
  • 黑马程序员JAVA基础Java 集合之Set 接口

      Set 集合的功能和Collection 是一致的,它没有提供任何额外的方法。实际上就是Collection ,只是行为不同(Set 不允许包含重复元素)。

      Set 集合的特点:存入的元素的是无序的,即存入和取出的顺序不一定不一致,且元素不可以重复。 

     1 public class SetText {
     2     public static void main(String[] args) 
     3     {
     4         Set books = new HashSet() ; 
     5 //        添加一个字符串对象
     6         books.add(new String("笑傲江湖"))  ; 
     7 //        添加另一的字符串对象,打印返回值
     8         System.out.println(books.add(new String("笑傲江湖")));
     9 //        打印Set集合中的元素。
    10         System.out.println(books); 
    11     }
    12 }

      Set 不允许包含相同的元素,所以在添加元素时,会用equals 方法进行判断,如果比较返回true则表示两对象相等,所以add 方法会返回false,表示添加失败

    一:HashSet 类

      HashSet 类是 Set 接口的实现类。其底层数据结构是哈希表,所以具有很好的存取和查找性能。 

      1.1、HashSet 是如何保证元素唯一性的?

      当向HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的HashCode值,然后根据该HashCode值来决定该对象在HashSet 中存储的位置。如果两个HashCode值不相等,则存入元素。(不会调用equals() 方法 。)

      如果两个HashCode值相等,则调用equals 方法进行判断,如果两个元素通过equals 方法比较返回true ,则添加失败,否则添加成功。

      所以:HashSet 集合判断两个元素相等的标准是两个对象通过equals 方法比较,并且两个对象的hashCode() 方法返回值也相等。在定义类时,如果要用到集合,则要重写hashCode() 和 equals() 两个方法。 

      注意:重写hashCode() 方法的基本规则:

      1、当两个对象通过equals 方法比较返回true 时,这两个对象的HashCode 应该相等。

      2、对象中用作equals 比较的标准属性,都应该用来计算HashCode值。

      不同类型属性获取HashCode值的方式:f表示对象中每一个有意义的属性。

    属性类型 计算方式
    boolean  HashCode = (f?0:1);
    整数型 hashCode = (int)f
    long   hashCode = (int)(f^(f>>>32))
    float hashCode = Float.floatToIntBits(f)
    double

    long l = Double.doubleToLongBits(f)

    hashCode = (int)(l^(l>>>32)) 

    普通引用型 hashCode = f.hashCode() 
     
     
     1 class Person
     2 {
     3     private String name ; 
     4     private String id  ; 
     5     public Person(String name , String id)
     6     {
     7         this.name = name ; 
     8         this.id = id ; 
     9     }
    10 //    get方法
    11     public String getName()
    12     {
    13         return name ;
    14     }
    15     public String getId()
    16     {
    17         return id ; 
    18     }
    19 //    重写equals 方法
    20     public boolean equals(Object obj)
    21     {
    22 //        
    23         if (!(obj instanceof Person))
    24             return false  ;
    25         Person p = (Person) obj ; 
    26         return this.name.equals(p.name) && this.id.equals(p.id) ; 
    27     }
    28 //    重写 hashCode 方法
    29     public int hashCode()
    30     {
    31         return name.hashCode()+id.hashCode() ; 
    32     } 
    33 }
    34 public class HashSetDemo {
    35     public static void main(String[] args)
    36     {
    37         HashSet hs = new HashSet() ; 
    38         hs.add(new Person("张三" , "110110")) ;
    39         hs.add(new Person("李四" , "120110")) ;
    40         hs.add(new Person("张三" , "123110")) ;  
    41         hs.add(new Person("张三" , "110110")) ;  
    42         Iterator it = hs.iterator() ; 
    43         while(it.hasNext())
    44         {
    45             Person p = (Person) it.next() ; 
    46             System.out.println("name:"+p.getName() + "," + "id:"+p.getId() );
    47         } 
    48     }
    49 }

      上面代码的判断两个Person 对象是否相等的条件是: name 和 id 都相等。

      1.2、关于hashCode() 方法对于HashSet 的作用:

      hash 算法功能:它能保证通过一个对象快速查找到另一个对象。其价值在于速度,它可以保证查询得到快速执行。当需要集合中的某个集合中某个元素时,hash算法可以直接根据元素的值得到该元素保存在何处,从而可以让程序快速找到该元素。简单来来说HashCode值 类似数组中的索引。

      而不直接使用数组的原因:因为数组的索引是连续的,长度是固定的,无法自由增加数组的长度,而HashSet 采用每个元素的HashCode 值作为索引,可以自由增加HashSet 长度。而且因为长度不是固定的,索引不是连续的,所以HashSet 存储和删除都是比较高效的。

      1.3、HashSet 判断和删除的依据:

      调用hashCode() 方法判断对象的hashCode值是否在集合中存在,如果不存在则返回false  ;否则再调用equals () 方法进行判断。

       

    二.TreeSet 类

      2.1 TreeSet 是如何保证元素唯一性的?

      当把一个对象添加到TreeSet时,如果是第一个,不会进行任何判断;当添加第二对象时,TreeSet 就会调用对象的compareTo(Object o) 方法与集合其他元素进行比较,通过comparaTo 方法返回的值来进行判断,如果compareTo 方法返回的是值是0, 这表示集合中存在该元素;如果返回的是1这表示该对象大于比较的对象;如果返回的是-1则表示该对象小于比较的对象。

      所以,如果试图把一个对象添加进TreeSet,则该对象必须实现Comparable 接口否则程序将会抛出异常---ClassCastException。而且向TreeSet 中添加的应该是同一类对象。

      TreeSet 类可以确保元素集合处于排序状态,这排序状态不是根据元素的插入顺序进行排序的,而是根据元素的实际值来排序的。

     1 public class TreeSetText {
     2     public static void main(String[] args)
     3     {
     4         TreeSet ts = new TreeSet() ;
     5         ts.add("adsfdf") ; 
     6         ts.add("refdas") ;
     7         ts.add("vceqw") ;
     8         ts.add("asdfe") ; 
     9         System.out.println(ts);
    10     }
    11 }

      输入结果:

    [adsfdf, asdfe, refdas, vceqw]

      与HashSet 集合采用的hash 算法来确定元素的存储的位置不同,TreeSet 采用二叉树的数据结构对元素进行排序。

      TreeSet排序方式用两种:

      1、自然顺序排序。

      2、定义比较器排序,即制定排序。

     2.1、自然排序:

      让元素自身具备比较性,元素需要实现Comparable 接口,覆盖compareTo 方法。

      > int compareTo(Object o) :比较对象与指定对象的顺序,如果该对象小于、等于或大于指定对象,则分为负整数、0或正整数。

     1 class Student implements Comparable
     2 {
     3 //    当两个对象的name 和 id 都相等时,表示是同一个人。
     4     private String name ;
     5     private long id ; 
     6     public Student(String name , long id)
     7     {
     8         this.name = name; 
     9         this.id = id ;
    10     }
    11 //    get方法
    12     public String getName()
    13     {
    14         return name ;
    15     }
    16     public long getId()
    17     {
    18         return id ; 
    19     }
    20 //    覆盖comparaTo 方法
    21 //    按找id来排序。
    22     public int compareTo(Object obj)  
    23     {
    24         if (obj instanceof Person)
    25             throw new RuntimeException() ;
    26         Student p = (Student) obj ;  
    27         if(this.id > p.id  )
    28             return 1 ; 
    29         if(this.id == p.id )
    30         {
    31             return this.name.compareTo(p.name) ;
    32         }
    33         return -1 ;
    34     } 
    35     public boolean equals(Object obj)
    36     {
    37         Student p = (Student) obj ; 
    38         return this.name.equals(p.name)&& p.id == this.id ; 
    39     }
    40 }

      当Student 对象添加进TreeSet 时, 会调用Student 类的compareTo(Object o) 方法来进行判断要添加的对象是否在集合中已经存在,如果存在则不再添加;如果不存在,则根据compareTo 方法的返回值来进行排序。

      

      2.3 定义比较器(定制排序)

      当对象自身不具备比较性时,或者具备的比较性不是所需要的,这时候就需要让集合自身具备比较性。如:TreeSet 的自然排序是根据集合元素的大小,TreeSet 将它们以升序排序的,如果要定制需要的排序,例如以降序排序,则可以通过定义比较器的方法让集合自身具备比较性。

      如何定义比较器:

      定义一个类,实现Comparator 接口,并且覆盖compare() 方法。然后将比较器对象作为参数传递给TreeSet 集合的构造器。

      > int compare(Object o1 , Object o2) 比较用来排序的两个参数。根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。

      注意:其实Comparator 接口除了compare 方法外,还有equals(Object o),由于定义的类继承了Object 类, 所以不需要覆盖equals 方法了。 

     1 class Student implements Comparable
     2 {
     3 //    当两个对象的name 和 id 都相等时,表示是同一个人。
     4     private String name ;
     5     private long id ; 
     6     public Student(String name , long id)
     7     {
     8         this.name = name; 
     9         this.id = id ;
    10     }
    11 //    get方法
    12     public String getName()
    13     {
    14         return name ;
    15     }
    16     public long getId()
    17     {
    18         return id ; 
    19     }
    20 //    覆盖comparaTo 方法
    21 //    按找id来排序。
    22     public int compareTo(Object obj)  
    23     {
    24         if (obj instanceof Person)
    25             throw new RuntimeException() ;
    26         Student p = (Student) obj ;  
    27         if(this.id > p.id  )
    28             return 1 ; 
    29         if(this.id == p.id )
    30         {
    31             return this.name.compareTo(p.name) ;
    32         }
    33         return -1 ;
    34     } 
    35     public boolean equals(Object obj)
    36     {
    37         Student p = (Student) obj ; 
    38         return this.name.equals(p.name)&& p.id == this.id ; 
    39     }
    40 }
    41 public class TreeSetTest {
    42     public static void main(String[] args)
    43     {
    44         TreeSet ts = new TreeSet( new Comparator()
    45         {
    46 //            定义比较器:按id的降序排列
    47             public int compare(Object o1 , Object o2)
    48             {
    49                 if ((o1 instanceof Person) &&(o2 instanceof Person))
    50                     throw new RuntimeException() ;
    51                 Student p = (Student) o1 ;
    52                 Student s = (Student) o2 ;
    53                 if(p.getId() > s.getId()  )
    54                     return -1 ; 
    55                 if(p.getId() == s.getId() )
    56                 {
    57                     return p.getName().compareTo(s.getName()) ;
    58                 }
    59                 return  1 ;
    60             }
    61         }) ; 
    62         
    63         ts.add(new Student("hezuoan001",12)) ;
    64         ts.add(new Student("hezuoan005",11)) ;
    65         ts.add(new Student("hezuoan003",1)) ;
    66         ts.add(new Student("hezuoan004",4)) ;
    67         ts.add(new Student("hezuoan002",11)) ;
    68         
    69         Iterator it = ts.iterator() ; 
    70         while(it.hasNext())
    71         {
    72             Student s = (Student) it.next() ; 
    73             System.out.println("name:"+s.getName()+"    id:"+s.getId());
    74         }
    75     }
    76 } 

       当没有定义比较器时(第44行没有向TreeSet 集合构造器传递参数 )打印的结果如下: 

    name:hezuoan003    id:1
    name:hezuoan004    id:4
    name:hezuoan002    id:11
    name:hezuoan005    id:11
    name:hezuoan001    id:12

       定义比较器时,且在Student 类实现了Comparable 接口并覆盖了compareTo(Object o) 方法的情况下,打印的结果如下:

    name:hezuoan001    id:12
    name:hezuoan002    id:11
    name:hezuoan005    id:11
    name:hezuoan004    id:4
    name:hezuoan003    id:1

       所以,当两种排序都存在时,以比较器为主。

       注意:当通过Comparator 对象来实现TreeSet 定制排序时,依然不可以向TreeSet 中添加类型不同的对象。

       2.4 练习:按字符串的长度排序: 

      字符串本身具备比较性:字符串实现了Comparable 接口,并且覆盖了CompareTo(Object obj) 方法,其比较是按自然循序排序的。而我们想要的排序是按字符串的长度来排序,所以这时候只能使用比较器。代码如下: 

     1 public class ComparatorTest {
     2     public static void main(String[] args)
     3     {
     4 //        定义比较器:按字符串的长度排序
     5         TreeSet ts = new TreeSet(new Comparator()
     6         {
     7             public int compare(Object o1 , Object o2)
     8             {
     9                 String s1 = (String) o1 ; 
    10                 String s2 = (String) o2 ;
    11 //                定义 Integer 对象来进行 s1 和 s2 长度的比较。
    12                 int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
    13                 if (num == 0)
    14                 {
    15                     return s1.compareTo(s2) ; 
    16                 }
    17                 return num ;
    18             }
    19         }) ; 
    20         ts.add("adfwefad");
    21         ts.add("adfadsfwefawefad");
    22         ts.add("adfwfadfaefad");
    23         ts.add("adfwedfafad");
    24         ts.add("adfwedfasfad"); 
    25         for (Object obj : ts)
    26         { 
    27             System.out.println(obj);
    28         } 
    29     }
    30 }

      总结:

      Java 的一些常用类已经实现了 Comparable 接口,并提供了比较大小的标准。下面是实现了Comparable 接口的常用类:

      > 所有数值类型对应的包装类:Integer、Double等,按他们的数值进行大小比较。

      > Character : 按字符的UNICODE 值进行比较。

      > Boolean : true 对应的包装类实例大于false 对应的包装实例。

      > String : 按字符串中字符的UNICODE 值进行比较。

      > Date、Time : 后面的时间、日期比前面的时间、日期大。

       

      2.5 TreeSet 提供的额外方法:

      因为TreeSet 中的元素是有序的,所以增加了访问第一个、前一个、后一个、最后一个元素的方法,并且提供了三个从TreeSet 中截取子TreeSet 的方法。如下:

      > Object first() : 返回第一个元素。

      > Object last() : 返回最后一个元素。

      > Object lower(Object o) : 返回集合中位于指定元素之前的元素。

      > Object higher(Object o) : 返回集合中位于指定元素之后的元素。

      > SortedSet subSet(fromElement , toElement) : 返回此Set 的子集合 ,范围从fromElement(包含)到toElement(不包含)。

      > SortedSet headSet(toElement) : 返回此Set 子集合,由小于toElement 的元素组成。

      > SortedSet tailSet(fromElement) : 返回次Set 子集合,有大于fromElement 的元素组成。

  • 相关阅读:
    使用svn diff的-r参数的来比较任意两个版本的差异
    mysql client常见error总结
    mysql 中 unix_timestamp,from_unixtime 时间戳函数
    hyperledger explorer 结合 fabric1.4 搭建 区块链浏览器 踩坑记录
    fabric1.4 网络操作
    通过配置连接池大小来提升性能
    docker基本操作及介绍
    InnoDB 引擎中的索引类型
    MySQL互联网业务使用建议
    mysql InnoDB引擎是否支持hash索引
  • 原文地址:https://www.cnblogs.com/jbelial/p/2988765.html
Copyright © 2011-2022 走看看