Java Set集合(HashSet、TreeSet)


 什么是HashSet?操作過程是怎么樣的?

  1、HashSet底層實際上是一個HashMap,HashMap底層采用了哈希表數據結構

  2、哈希表又叫做散列表,哈希表底層是一個數組,這個數組中每一個元素是一個單向鏈表,每個單向鏈表都有一個獨一無二的hash值,代表數組的下標。在某個單向鏈表中的每一個節點上的hash值是相同的。hash值實際上是key調用hashCode方法,再通過"hash function"轉換成的值

  3、如何向哈希表中添加元素?

    先調用被存儲的key的hashCode方法,經過某個算法得出hash值,如果在這個哈希表中不存在這個hash值,則直接加入元素。如果該hash值已經存在,繼續                調用Key之間的equals方法,如果equals方法返回false,則將該元素添加。如果equals方法返回true,則放棄添加該元素

    HashMap和HashSet初始化容量是16,默認加載因子是0.75

HashSet的數據結構

  獻上我看視頻截的圖

代碼舉例

 1 public class Test{
 2     
 3     public static void main(String[] args) {
 4         
 5         Set set = new HashSet();
 6         
 7         Student stu1 = new Student("1", "JACK");
 8         Student stu2 = new Student("2", "TOM");
 9         Student stu3 = new Student("3", "JIM");
10         
11         set.add(stu1);
12         set.add(stu2);
13         set.add(stu3);
14         
15         System.out.println("size :" + set.size());
16     }
17     
18
19     
20 }
21 
22 class Student{
23     String no;
24     String name;
25     
26     Student(String no, String name){
27         this.no = no;
28         this.name = name;
29     }
30 }

  這個的輸出結果顯而易見是3,因為我們添加了三個元素,但是如果改一下

1 Student stu1 = new Student("1", "JACK");
2 Student stu2 = new Student("1", "JACK");
3 Student stu3 = new Student("3", "JIM");
4 
5 System.out.println(stu1.hashCode());
6 System.out.println(stu2.hashCode());

  可以運行試一下,stu1和stu2的hashCode是不一樣的,為什么呢?因為這兩個對象是New出來的,引用地址不一樣。我們不希望出現這樣的情況,那就要重寫hashCode和equals方法

 1 class Student{
 2     String no;
 3     String name;
 4     
 5     Student(String no, String name){
 6         this.no = no;
 7         this.name = name;
 8     }
 9     
10     public boolean equals(Object o){
11         if(this == o) return true;
12         if(o instanceof Student){
13             Student student = (Student) o;
14             if(student.no.equals(this.no) && student.name.equals(this.name)) return true;
15         }
16         
17         return false;
18     }
19     
20     public int hashCode(){
21         return no.hashCode();
22     }
23 }

  再次運行,插入兩個數據一樣的對象,就不會重復了

 

TreeSet

  treeset實現了sortedset接口,有個很重要的特點是里面的元素都是有序的

 1 public class Test{
 2     
 3     public static void main(String[] args) {
 4         
 5         SortedSet set = new TreeSet();
 6         
 7         set.add(1);
 8         set.add(100);
 9         set.add(50);
10         
11         System.out.println(set);
12     }
13 
14 }

  輸出結果:[1, 50, 100]

  那如果我們自定義類可以進行比較嗎?

 1 public class Test{
 2     
 3     public static void main(String[] args) {
 4         
 5         SortedSet set = new TreeSet();
 6         
 7         Student stu1 = new Student(22);
 8         Student stu2 = new Student(11);
 9         Student stu3 = new Student(100);
10         
11         set.add(stu1);
12         set.add(stu2);
13         set.add(stu3);
14         
15         System.out.println(set);
16     }
17 
18 }
19 
20 class Student{
21     int age;
22     
23     Student(int age){
24         this.age = age;
25     }
26 }

  這樣運行會出現一個問題,報ClassCastException,這就需要來看看源碼了,底層到底是怎么實現的,為什么自定義類就先不行呢?

  這里可以看到,當我們在創建一個TreeSet的時候,實際上是new了一個TreeMap

  add的時候調用了TreeMap的put方法,來看看TreeMap中的put方法

 

   可以看到第三個紅框那里,會對key進行一個強制類型轉換,我們上面的代碼肯定是就是在這里裝換不成功,Comparable是什么?來看看API

  是一個接口,從翻譯就可以看出,只要實現了這個接口,就是可以比較的,下面是實現了這個接口的類

  隨便框了幾個,上面就有Integer,這個類實現了comparable接口,因此第一個代碼是正確的,現在我們是不是只要實現這個接口就好了呢!

 1 class Student implements Comparable{
 2     int age;
 3     
 4     Student(int age){
 5         this.age = age;
 6     }
 7 
 8     //重寫接口中的方法
 9     //要重寫比較規則
10     @Override
11     public int compareTo(Object o) {
12         // TODO Auto-generated method stub
13         int age1 = this.age;
14         int age2 = ((Student)o).age;
15         return age2 - age1;
16     }
17 }

  這樣OK了,再來看看上面有紅框的源碼,cmp<0,cmp>0,left,right都是什么東西,二叉樹!所以底層是通過二叉樹來排序的

  如果是自定義類中的String呢,那就return String的compareTo方法就好了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM