Java hashset,進階07 Set接口、HashSet、LinkedHashSet

 2023-11-30 阅读 22 评论 0

摘要:java.util.Set接口和java.util.List接口一樣,同樣繼承自Collection接口,它與Collection接口中的方法基本一致,并沒有對Collection接口進行功能上的擴充,只是比Collection接口更加嚴格了。與List接口不同的是,Set接口中元素無序,并

在這里插入圖片描述

java.util.Set接口和java.util.List接口一樣,同樣繼承自Collection接口,它與Collection接口中的方法基本一致,并沒有對Collection接口進行功能上的擴充,只是比Collection接口更加嚴格了。與List接口不同的是,Set接口中元素無序,并且都會以某種規則保證存入的元素不出現重復。

java.util.Set接口 extends Collection接口
Set接口的特點:
1.不允許存儲重復的元素
2.沒有索引,沒有帶索引的方法,也不能使用普通的for循環遍歷

Set集合有多個子類,這里我們介紹其中的java.util.HashSet、java.util.LinkedHashSet這兩個集合。

tips:Set集合取出元素的方式可以采用:迭代器、增強for。

HashSet集合介紹

Java hashset。java.util.HashSet集合 implements Set接口
HashSet特點:
1.不允許存儲重復的元素
2.沒有索引,沒有帶索引的方法,也不能使用普通的for循環遍歷
3.是一個無序的集合,存儲元素和取出元素的順序有可能不一致
4.底層是一個哈希表結構(查詢的速度非常的快)

public static void main(String[] args) {Set<Integer> set = new HashSet<>();//多態//使用add方法往集合中添加元素set.add(1);set.add(3);set.add(2);set.add(1);//重復,不允許存儲//使用迭代器遍歷set集合Iterator<Integer> it = set.iterator();while (it.hasNext()){Integer n = it.next();System.out.println(n);//1,2,3}//使用增強for遍歷set集合System.out.println("-----------------");for (Integer i : set) {System.out.println(i);}
}

HashSet集合存儲數據的結構(哈希表)

什么是哈希表呢?
在JDK1.8之前,哈希表底層采用數組+鏈表實現,即使用鏈表處理沖突,同一hash值的鏈表都存儲在一個鏈表里。但是當位于一個桶中的元素較多,即hash值相等的元素較多時,通過key值依次查找的效率較低。而JDK1.8中,哈希表存儲采用數組+鏈表+紅黑樹實現,當鏈表長度超過閾值(8)時,將鏈表轉換為紅黑樹,這樣大大減少了查找時間。

先了解哈希值:

哈希值:是一個十進制的整數,由系統隨機給出(就是對象的地址值,是一個邏輯地址,是模擬出來得到地址,不是數據實際存儲的物理地址)

在Object類有一個方法,可以獲取對象的哈希值
int hashCode() 返回該對象的哈希碼值。
hashCode方法的源碼:
public native int hashCode();
native:代表該方法調用的是本地操作系統的方法

public class Person extends  Object{//先讓Person類為空,默認繼承Object,便于使用hashcode方法,然后重寫//重寫hashCode方法@Overridepublic int hashCode() {return  1;}
}public static void main(String[] args) {//Person類繼承了Object類,所以可以使用Object類的hashCode方法Person p1 = new Person();int h1 = p1.hashCode();System.out.println(h1);//重寫hashcode之前 1967205423  | 重寫后 1Person p2 = new Person();int h2 = p2.hashCode();System.out.println(h2);//42121758   |  1/*toString方法的源碼:return getClass().getName() + "@" + Integer.toHexString(hashCode());*/System.out.println(p1);//com.itheima.demo03.hashCode.Person@75412c2fSystem.out.println(p2);//com.itheima.demo03.hashCode.Person@282ba1eSystem.out.println(p1==p2);//false   如果重寫了hashcode都是返回1,上面對象的返回值一樣// 但此處顯示false 也就是說實際的物理地址不相等//特殊:string類重寫了hashcode方法/*String類的哈希值String類重寫Obejct類的hashCode方法*/String s1 = new String("abc");String s2 = new String("abc");System.out.println(s1.hashCode());//96354System.out.println(s2.hashCode());//96354//特殊下面兩個雖然字符串不一樣,但是哈希值一樣System.out.println("重地".hashCode());//1179395System.out.println("通話".hashCode());//1179395
}

HashSet集合存儲數據的結構圖
在這里插入圖片描述

hashset contains。注意hashset的構成, 初始容量是16
首先計算數據的哈希值,把相同哈希值的數據放到同一位置上,其哈希值也就是對應的查詢地址,
鏈表長度超過8位就把鏈表轉換為紅黑樹(根和葉子都是黑色,中間是紅色),為了提高查詢的速度。

Set集合不允許存儲重復元素的原理:

public static void main(String[] args) {//創建HashSet集合對象HashSet<String> set = new HashSet<>();String s1 = new String("abc");String s2 = new String("abc");set.add(s1);set.add(s2);set.add("重地");set.add("通話");//重地、通話 兩者的哈希值相同set.add("abc");System.out.println(set);//注意:集合重寫了toString方法,直接打印里面的內容//[重地, 通話, abc]
}

創建了hashset集合
哈希表的數據結構組成:數組+鏈表 或者 數組+紅黑樹, 橫著是數組結構,豎著是鏈表結構(結合上面的hashset集合存儲圖)
在這里插入圖片描述

原理:set在用add方法的時候會調用元素的hashcode方法和equals方法
先計算哈希值,放到數組中,如果遇到相同哈希值就進行比較,如果內容相同就不會添加到一起,
如果哈希值相同,內容不同(如上述 重樹和通話)就添加到一起,放到數組下面的鏈表當中。

HashSet存儲自定義類型元素

給HashSet中存放自定義類型元素時,需要重寫對象中的hashCode和equals方法,建立自己的比較方式,才能保證HashSet集合中的對象唯一
之前學過的Integer類等都是重寫過hashCode和equals方法。

首先定義Person類,有參數無參數的構造方法,成員變量的get,set。
再重寫toString方法public class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
public static void main(String[] args) {//創建HashSet集合存儲PersonHashSet<Person> set = new HashSet<>();Person p1 = new Person("張小敬",18);//42121758Person p2 = new Person("張小敬",18);//20671747Person p3 = new Person("李必", 19);System.out.println(p1.hashCode());System.out.println(p2.hashCode());//上述p1,p2哈希值不同,說明沒有重寫hashcode方法和equals方法的對象不是同一個對象,// 雖然內容相同,不能保證唯一System.out.println(p1 == p2);//false  == 比較的是地址值System.out.println(p1.equals(p2));//false  沒有重寫比較的是兩個的地址值。//重寫后的結果:/*748923221748923221falsetrue*///重寫了hashcode和equals方法后添加到結合中,就實現了唯一性set.add(p1);set.add(p2);set.add(p3);System.out.println(set);//[Person{name='李必', age=19}, Person{name='張小敬', age=18}]
}

LinkedHashSet

HashSet可以保證元素唯一,但是存進去的元素是沒有順序的,要保證有序就要用到子類
LinkedHashSet ,由鏈表和哈希表組合的一個數據存儲結構。
java.util.LinkedHashSet集合 extends HashSet集合

LinkedHashSet集合特點:

   底層是一個哈希表(數組+鏈表/紅黑樹)+鏈表:

多了一條鏈表(記錄元素的存儲順序),保證元素有序

public static void main(String[] args) {HashSet<String> set = new HashSet<>();set.add("www");set.add("abc");set.add("abc");set.add("itcast");System.out.println(set);//[abc, www, itcast] 無序,不允許重復LinkedHashSet<String> linked = new LinkedHashSet<>();linked.add("www");linked.add("abc");linked.add("abc");linked.add("itcast");System.out.println(linked);//[www, abc, itcast] 有序,不允許重復
}

可變參數

hashset的add方法。可變參數:是JDK1.5之后出現的新特性
使用前提:
當方法的參數列表數據類型已經確定,但是參數的個數不確定,就可以使用可變參數.
使用格式:定義方法時使用

修飾符 返回值類型 方法名(數據類型…變量名){}

可變參數的原理:
可變參數底層就是一個數組,根據傳遞參數個數不同,會創建不同長度的數組,來存儲這些參數
傳遞的參數個數,可以是0個(不傳遞),1,2…多個

 public static void main(String[] args) {//int i = add();//int i = add(10);int i = add(10,20);//int i = add(10,20,30,40,50,60,70,80,90,100);System.out.println(i);method("abc",5.5,10,1,2,3,4);}/*可變參數的注意事項1.一個方法的參數列表,只能有一個可變參數2.如果方法的參數有多個,那么可變參數必須寫在參數列表的末尾*//*public static void method(int...a,String...b){}*//*public static void method(String b,double c,int d,int...a){}*///可變參數的特殊(終極)寫法public static void method(Object...obj){}/*定義計算(0-n)整數和的方法已知:計算整數的和,數據類型已經確定int但是參數的個數不確定,不知道要計算幾個整數的和,就可以使用可變參數add(); 就會創建一個長度為0的數組, new int[0]add(10); 就會創建一個長度為1的數組,存儲傳遞來過的參數 new int[]{10};add(10,20); 就會創建一個長度為2的數組,存儲傳遞來過的參數 new int[]{10,20};add(10,20,30,40,50,60,70,80,90,100); 就會創建一個長度為2的數組,存儲傳遞來過的參數 new int[]{10,20,30,40,50,60,70,80,90,100};*/public static int add(int...arr){//System.out.println(arr);//[I@2ac1fdc4 底層是一個數組//System.out.println(arr.length);//0,1,2,10//定義一個初始化的變量,記錄累加求和int sum = 0;//遍歷數組,獲取數組中的每一個元素for (int i : arr) {//增強for 定義一個變量,用來取list中的值,它的類型就是數組的或者集合中存放的類型。//累加求和sum += i;}//把求和結果返回return sum;}//定義一個方法,計算三個int類型整數的和
public static int add3(int a ,int b, int c){return a+b+c;
}//定義一個方法,計算兩個int類型整數的和public  static  int add2(int a ,int b){return a+b;}小結:1、注意可變參數的注意事項,1.一個方法的參數列表,只能有一個可變參數2.如果方法的參數有多個,那么可變參數必須寫在參數列表的末尾2、可變參數的底層就是一個可變的數組,隨著加入元素增加長度

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/186171.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息