Java中接口Set的特點(diǎn)及方法說(shuō)明
接口Set的特點(diǎn)及方法
1、特點(diǎn):無(wú)序,不可重復(fù);
2、實(shí)現(xiàn)類:添加的方法:
add(Object obj);addAll(Collection c);
Set中沒(méi)有修改的方法,可以間接修改,先刪除再添加;
刪除的方法:
remove(Object obj);removeAll(Collection c);retainAll(Collection c)僅保留set中那些包含在指定的Collection中的元素;clear()清除所有元素;
查詢的方法:
contains(Object obj)查詢set中是否包含指定元素,包含返回true;containsAll(Collection c)查詢set中是否包含指定的多個(gè)元素,全部包含返回true;isEmpty()判斷set是否為空,為空返回true;
3、set集合的遍歷:使用for循環(huán);
使用iterator迭代器:it.hasNext()如果有下一個(gè)元素,返回true;
it.next()返回下一個(gè)元素;it.remove()刪除迭代器返回的最后一個(gè)元素;
Set接口及其實(shí)現(xiàn)類
Set接口有兩個(gè)實(shí)現(xiàn)類
- 散列集合:HashSet(Hash算法)
- 樹(shù)集合:TreeSet(二叉樹(shù)算法)
Set接口:Set存儲(chǔ)元素是無(wú)序不可以重復(fù)的
HashSet:元素是否重復(fù)是依據(jù):元素自身equals比較進(jìn)行判定的。TreeSet:元素是否重復(fù)是依據(jù):CompareTo方法返回是否為0。
因?yàn)镾et接口也是Collection的子接口
所以也可以使用Collection實(shí)現(xiàn)的所有方法,其中常用的有:
- 添加:add()
- 刪除:remove()
- 檢查元素是否存在:contains(Object o)
- 迭代器:iterator()
1、TreeSet:樹(shù)狀集合、存放有序
語(yǔ)法:Set<E> set = new TreeSet<E>();
說(shuō)明:TreeSet添加元素時(shí),首先按照compareTo()進(jìn)行比較,而TreeSet要想指定集合的存放順序,被排序的對(duì)象需實(shí)現(xiàn)Comparable接口,這個(gè)接口強(qiáng)行的對(duì)每個(gè)實(shí)現(xiàn)類對(duì)象進(jìn)行整體的排序,這種排序稱為類的自然排序,這個(gè)接口有個(gè)抽象方法compareTo,該方法稱為自然排序的方法。
向TreeSet中添加的元素必須是同一個(gè)類的。(否則底層調(diào)用compareTo時(shí)會(huì)報(bào)類型轉(zhuǎn)換異常)
public class Person implements Comparable{
int id;
int age;
String name;
public Person(int id,int age,String name){
super();
this.id = id;
this.age = age;
this.name = name;
}
public String toString(){
return "Person [id = " + id + ", age = " + age + ", name = " + name + "]";
}
@Override
public int compareTo(Object o){//按照id排序
Person p;
if(o instanceof Pserson){
p = (Person)o;
}else{
return -1;//-1代表傳入的參數(shù)比我本身要小
}
int diff = this.id - p.id;
if(diff!=0){
diff = diff / Math.abs(diff);//差值除以本身絕對(duì)值,可以得到+1或-1的值。
}
return diff;
}
該compareTo方法是根據(jù)對(duì)象的id進(jìn)行比較,也可以根據(jù)自己的需求自定義比較內(nèi)容:
this.id是當(dāng)前對(duì)象的ido.id是指定對(duì)象的id(也就是傳入對(duì)象的id)
調(diào)用sort方法時(shí)可以自定義升序或降序:
- 升序:this.id - o.id(this.id < o.id 結(jié)果為負(fù)、this.id > o.id 結(jié)果為正,this.id = o.id 結(jié)果為0)
- 降序:o.id - this.id(this.id < o.id 結(jié)果為正、this.id < o.id 結(jié)果為負(fù),this.id = o.id 結(jié)果為0)
上述代碼中,我們使用的是this.id - o.id,所以我們是升序排序的(默認(rèn)情況就是升序排序的)。
import java.util.Set;
import java.util.TreeSet;
public class Demo{
public static void main(String[] args){
Set set = new TreeSet();
Person p1 = new Person(1,18,"小明");
Person p2 = new Person(2,5,"小強(qiáng)");
Person p3 = new Person(3,20,"小張");
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p3);//重復(fù)的元素不會(huì)被添加到集合中
System.out.println(set.size());
Iterator it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
compareTo方法就是為了實(shí)現(xiàn)Comparable接口而存在的,而實(shí)現(xiàn)Comparable接口就是為了可以直接使用sort()方法對(duì)該對(duì)象進(jìn)行自定義排序。
注意:TreeSet是不能存在null元素(HashSet是可以存儲(chǔ)null值的),向TreeSet中添加元素時(shí),首先按照compareTo()進(jìn)行比較,元素底層調(diào)用compareTo方法時(shí)會(huì)報(bào)空指針異常。
2、HashSet:散列集合、高效快速
語(yǔ)法:Set<E> set = new HashSet<E>();
說(shuō)明:HashSet存儲(chǔ)的對(duì)象,應(yīng)該重寫(xiě)hashCode()和equals()兩個(gè)方法,在添加元素的時(shí)候會(huì)根據(jù)我們重寫(xiě)hashCode()方法計(jì)算元素的hash值,根據(jù)哈希值計(jì)算元素存儲(chǔ)的位置(哈希地址),如果該哈希地址沒(méi)有元素則會(huì)直接添加成功,如果該位置有其他元素會(huì)根據(jù)equals方法判斷是否為同一個(gè)對(duì)象,如果結(jié)果返回true則不會(huì)添加,如果為false則會(huì)以鏈表的形式追加到該哈希地址處。(底層根據(jù)HashMap實(shí)現(xiàn))
在不指定泛型的情況下可以為不同的類型的值;
public class Person{
int id;
String name;
public Person(int id, String name) {
super();
this.id = id;
this.name = name;
}
//重寫(xiě)hashCode方法只根據(jù)id進(jìn)行計(jì)算hash值
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
//重寫(xiě)equals方法
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (id != other.id)
return false;
return true;
}
//重寫(xiě)toString方法,方便在我們輸出的時(shí)候查看
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
}
public class Demo{
public static void main(String[] args){
Set set = new HashSet();
Person p1 = new Person(1,"小明");
Person p2 = new Person(2,"小張");
Person p3 = new Person(3,"小李");
set.add(p1);
set.add(p2);
set.add(p3);
//在我們添加成功以后修改其決定hash值的id
p2.id = 5;
//在刪除時(shí)會(huì)導(dǎo)致刪除不掉(如果修改的是name則不會(huì)出現(xiàn)刪除不掉的效果)
//因?yàn)橹暗脑卮鎯?chǔ)在id為2計(jì)算的哈希地址的位置,現(xiàn)在是去id為5計(jì)算的哈希地址出去刪除,所以刪除不掉
//set.remove(p2);
//會(huì)添加到id為5計(jì)算的哈希地址,所以會(huì)添加成功(雖然屬性相同,但是之前的是存儲(chǔ)在根據(jù)id為2計(jì)算的哈希地址位置)
set.add(p2);
//當(dāng)我們繼續(xù)添加的時(shí)候,equals返回的是true,所以不會(huì)添加成功
set.add(p2);
//此時(shí)創(chuàng)建一個(gè)id為2的對(duì)象繼續(xù)添加
Person p4 = new Person(2,"小張");
set.add(p4);//會(huì)添加id為2計(jì)算的哈希地址位置,雖然該位置有元素,但是之前的元素已經(jīng)被修改了,所以會(huì)添加成功
System.out.println("集合的長(zhǎng)度為:"+set.size());
Iterator it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
輸出結(jié)果:
集合的長(zhǎng)度:5
Person [id=1, name=小明] //根據(jù)id為1計(jì)算的哈希地址
Person [id=5, name=小張] //根據(jù)id為2計(jì)算的哈希地址(原p2對(duì)象以id為2的哈希地址進(jìn)行存儲(chǔ),但是后來(lái)id被修改為5了)
Person [id=2, name=小張] //根據(jù)id為2計(jì)算的哈希地址(p4對(duì)象)
Person [id=3, name=小李] //根據(jù)id為3計(jì)算的哈希地址
Person [id=5, name=小張] //根據(jù)id為5計(jì)算的哈希地址(修改以后的p2以id為5計(jì)算的哈希地址存儲(chǔ))
HashSet存儲(chǔ)元素的存儲(chǔ)過(guò)程:
首先說(shuō)一下HashSet存儲(chǔ)對(duì)象的方式:首先根據(jù)我們重寫(xiě)的HashCode方法計(jì)算出Hash值,根據(jù)Hash值來(lái)判斷存入Set中的元素是否重復(fù)和存儲(chǔ)在Set集合中的哈希地址,如果該Hash地址為空,則會(huì)直接將該對(duì)象存儲(chǔ)到該哈希地址,如果該哈希地址有元素,會(huì)根據(jù)equals方法判斷是否為同一個(gè)對(duì)象,如果結(jié)果返回true,說(shuō)明兩個(gè)對(duì)象是同一對(duì)象,則不會(huì)添加該對(duì)象,如果返回的是false,說(shuō)明元素本身是不同的,則會(huì)以鏈表的形式同時(shí)存儲(chǔ)到該哈希地址的位置。
總結(jié):說(shuō)一下上面的執(zhí)行過(guò)程,Set集合是根據(jù)我們重寫(xiě)的HashCode方法中的屬性,來(lái)計(jì)算Hash值,來(lái)判斷存入Set中的元素是否重復(fù)和存儲(chǔ)在Set集合中哈希地址,當(dāng)我們?cè)诖鎯?chǔ)之后再進(jìn)行修改決定計(jì)算Hash值的屬性時(shí),會(huì)導(dǎo)致程序出現(xiàn)意想不到的結(jié)果,我們首先根據(jù)id為2計(jì)算出了一個(gè)存儲(chǔ)該對(duì)象的哈希地址,當(dāng)我們修改了id為5后,再進(jìn)行刪除操作時(shí),它會(huì)根據(jù)id為5計(jì)算一個(gè)Hash值,去該Hash值對(duì)應(yīng)的哈希地址去刪除元素,因?yàn)槲覀冎按鎯?chǔ)的哈希地址是根據(jù)id為2計(jì)算出來(lái)的,所以會(huì)導(dǎo)致本應(yīng)刪除的元素刪除不掉,而當(dāng)我們?cè)龠M(jìn)行add的時(shí)候,我們根據(jù)id為5的Hash值去存儲(chǔ)時(shí)發(fā)現(xiàn),id為5的地址并沒(méi)有元素,所以存儲(chǔ)成功,雖然和之前id為2時(shí)計(jì)算Hash值存儲(chǔ)位置的元素屬性值一樣,但是還是會(huì)存儲(chǔ)成功的,然而,當(dāng)我們?cè)賱?chuàng)建一個(gè)id為2的對(duì)象進(jìn)行存儲(chǔ)時(shí),雖然哈希地址相同,但此時(shí)之前根據(jù)該哈希地址的對(duì)象已經(jīng)改變了(id修改為5了),所以會(huì)將新創(chuàng)建的id為2的對(duì)象以鏈表的形式同時(shí)存儲(chǔ)在該哈希地址的位置,所以當(dāng)我們操作HashSet集合時(shí),不要去修改決定計(jì)算Hash值的元素屬性。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
一文帶你看懂Android動(dòng)畫(huà)的實(shí)現(xiàn)原理
動(dòng)畫(huà)是 Android 應(yīng)用程序中重要的交互特性,ndroid 提供了多種動(dòng)畫(huà)效果,包括平移、縮放、旋轉(zhuǎn)和透明度等,它們可以通過(guò)代碼或 XML 來(lái)實(shí)現(xiàn),本文將介紹 Android 動(dòng)畫(huà)的原理和實(shí)現(xiàn)方法,并提供一些示例,需要的朋友可以參考下2023-07-07
基于JAVA中使用Axis發(fā)布/調(diào)用Webservice的方法詳解
如果初識(shí)axis發(fā)布/調(diào)用WS,建議先讀上面的參考文件,本文對(duì)于發(fā)布/調(diào)用WS的主要步驟只是簡(jiǎn)單文字描述,沒(méi)有它寫(xiě)的詳盡2013-05-05
Java命令行運(yùn)行錯(cuò)誤之找不到或無(wú)法加載主類問(wèn)題的解決方法
這篇文章主要給大家介紹了關(guān)于Java命令行運(yùn)行錯(cuò)誤之找不到或無(wú)法加載主類問(wèn)題的解決方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01
java發(fā)送http的get、post請(qǐng)求實(shí)現(xiàn)代碼
下面小編就為大家?guī)?lái)一篇java發(fā)送http的get、post請(qǐng)求實(shí)現(xiàn)代碼。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-05-05
Java調(diào)用打印機(jī)的2種方式舉例(無(wú)驅(qū)/有驅(qū))
我們平時(shí)使用某些軟件或者在超市購(gòu)物的時(shí)候都會(huì)發(fā)現(xiàn)可以使用打印機(jī)進(jìn)行打印,這篇文章主要給大家介紹了關(guān)于Java調(diào)用打印機(jī)的2種方式,分別是無(wú)驅(qū)/有驅(qū)的相關(guān)資料,需要的朋友可以參考下2023-11-11
SpringBoot中的@Configuration注解詳解
這篇文章主要介紹了SpringBoot中的@Configuration注解詳解,Spring Boot推薦使用JAVA配置來(lái)完全代替XML 配置,JAVA配置就是通過(guò) @Configuration和 @Bean兩個(gè)注解實(shí)現(xiàn)的,需要的朋友可以參考下2023-08-08

