博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
黑马程序员-JAVA基础-Java 集合之Set 接口
阅读量:7220 次
发布时间:2019-06-29

本文共 10184 字,大约阅读时间需要 33 分钟。

------- android培训、java培训、期待与您交流! ----------

  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:1name:hezuoan004    id:4name:hezuoan002    id:11name:hezuoan005    id:11name:hezuoan001    id:12

 

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

name:hezuoan001    id:12name:hezuoan002    id:11name:hezuoan005    id:11name:hezuoan004    id:4name: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 的元素组成。

 

 

 

转载地址:http://euhym.baihongyu.com/

你可能感兴趣的文章
在Winform中使用Font Awesome
查看>>
003、nginx配置反向代理
查看>>
Android 动态改变Layout的大小
查看>>
oscache
查看>>
input标签placeholder 效果
查看>>
大整数的简单运算
查看>>
张清:写博客你要知道的事
查看>>
基于 Laravel 开发 ThinkSNS+ 中前端的抉择(webpack/Vue)踩坑日记【ThinkSNS+研发日记系列】...
查看>>
ACS-AAA server的配置
查看>>
android 中如何获取控件的宽和高
查看>>
一个解决问题的方法
查看>>
redhat配置centos网络yum源
查看>>
Redis的集群(故障转移)
查看>>
Redhat 7 防火墙常用配置
查看>>
iOS 视频播放地址
查看>>
集群软件分类
查看>>
Redis初探
查看>>
把spring-boot项目部署到外部tomcat环境下
查看>>
Usage of API documents as @since 1.7+ 错误解决
查看>>
Javascript 加载性能优化
查看>>