摘要:
在java中集合主要分為兩類阀蒂,collection和map兩個(gè)大的接口锦针,collection又分為list和set接口荠察,這里記錄set接口的基本使用奈搜,供自己隨時(shí)查看。
set的特點(diǎn):
set存儲(chǔ)的數(shù)據(jù)是無(wú)序的馋吗,不可重復(fù)的。set接口是collection的子接口宏粤,set接口沒(méi)有提供額外的方法灼卢,set元素不允許包含相同的元素(無(wú)序不可重復(fù)),set判斷兩個(gè)對(duì)象是不是相等不是使用==運(yùn)算符鞋真,而是根據(jù)equals方法
Set的主要實(shí)現(xiàn)類:
- HashSet(set接口是主要實(shí)現(xiàn)類,是線程不安全的沃于,可以存儲(chǔ)null值)
- LinkHashSet(是HashSet的子類涩咖,意思是在HashSet的基礎(chǔ)上添加了前后指針,使其遍歷時(shí)可以讓他按照添加的順序進(jìn)行遍歷繁莹,讓他看起來(lái)像是有序一樣檩互,但實(shí)際是一種假象)
- TreeSet(底層是二叉樹(shù),具體一點(diǎn)就是紅黑樹(shù)(樹(shù)結(jié)構(gòu)本身就是有序的)咨演,他要求放入treeset中的對(duì)象是同一個(gè)類的對(duì)象盾似,可以按照添加對(duì)象的指定屬性進(jìn)行排序)
set中常用的方法,Set接口沒(méi)有提供額外的方法雪标,因此set接口的操作方法都是在collection中定義的。
Set集合的基本使用示例
public class SetTest{
@Test
public void test() {
Set set = new HashSet();
set.add(456);
set.add(123);
set.add("AA");
set.add("CC");
set.add(new Person("tom", 123));
set.add(123);
set.add(new User("tom", 123));
set.add(new User("name",123));
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
// 打印的結(jié)果為:
AA
CC
Person{name='tom', age=123}
456
User{name='name', age=123}
123
User{name='tom', age=123}
set結(jié)構(gòu)的無(wú)序性:
得到的結(jié)果并不是按照添加的順序來(lái)的溉跃,然而這便不是巧合村刨,
其實(shí)每次遍歷的得到的結(jié)果都是這個(gè)值,并不是隨機(jī)性的撰茎,
這是因?yàn)榍段m然Set的數(shù)據(jù)最終也是通過(guò)數(shù)組存的
,但是在存儲(chǔ)的時(shí)候并不是按照數(shù)組的索引來(lái)添加的龄糊,
而是根據(jù)數(shù)據(jù)的hash值來(lái)決定的逆粹,這就是set的無(wú)序性。
加的元素不是按照數(shù)組中的順序一個(gè)個(gè)來(lái)的炫惩,而是通過(guò)魔種算法來(lái)確定添加的元素是
再數(shù)組中的哪一個(gè)位置
set結(jié)構(gòu)的不可重復(fù)性:
由例子中的結(jié)果可知僻弹,相同的元素并沒(méi)有被添加到set中,這是set不可重復(fù)性的體現(xiàn)他嚷,就是相同的元素只能添加一個(gè)蹋绽,具體的判斷方案是equals()方法不能返回true,如果是則添加不進(jìn)去。
向Set集合中添加元素的過(guò)程:
我們向HashSet中添加元素a筋蓖,首先調(diào)用元素a所在類的hansCode方法卸耘,計(jì)算元素a的hash值,此hash值接著通過(guò)某種算法算出再HashSet底層數(shù)組中存放的位置(即為相應(yīng)的索引位置)粘咖,判斷數(shù)組此位置上是否已經(jīng)有元素蚣抗,如果此位置沒(méi)有元素,則元素小a直接添加成功瓮下,如果有其他元素b(或者以鏈表形式存在的多個(gè)元素翰铡,)钝域,則比較
元素a和b的hash值,若果hash值不相同两蟀,則元素a添加成功网梢,如果hash相同,則需要調(diào)用元素a所再類的equals方法赂毯,如果返回true,則添加失敗战虏,如果返回false,則添加成功。
Set集合中注意事項(xiàng):
- 1HashSet集合判斷兩個(gè)元素相等的標(biāo)準(zhǔn)是兩個(gè)對(duì)象的hashCode方法比較 相等党涕,并且兩個(gè)對(duì)象的equals方法的返回值也是相等的。
- .2. 于存放在Set容器中的對(duì)象膛堤,對(duì)應(yīng)的類一定要重寫(xiě)equals()和hashCode方法。以實(shí)現(xiàn)對(duì)象的氙燈規(guī)則绿渣,即相等的對(duì)象必須有相等的散列碼
- 3 LinkedHashSet是作為HashSet的子類燕耿,再添加數(shù)據(jù)的同時(shí),每個(gè)數(shù)據(jù)還維護(hù)了兩個(gè)引用淀散,記錄次數(shù)據(jù)的前一個(gè)數(shù)組和后一個(gè)數(shù)組蚜锨,對(duì)于頻繁的遍歷操作,用LikedHashSet效率高
- 4 LinkedHashSet遍歷時(shí)是按照添加的順序遍歷的亚再,但是并不能說(shuō)LinkedHashSet是有序的,因?yàn)長(zhǎng)inkedHashSet在添加數(shù)據(jù)的時(shí)候還維護(hù)了兩個(gè)引用饲鄙。
- 5 之前提到Set中元素標(biāo)膠相等是通過(guò)hashCode方法和equals方法來(lái)決定的忍级,但是TreeSet作為Set接口的實(shí)現(xiàn)類卻是一個(gè)例外伪朽。TreeSet比較相是更具排序來(lái)決定的,而且TreeSet中添加的元素是同一類型的元素朴肺,因?yàn)樾枰容^大小,如果不是同一類型的元素就沒(méi)有辦法比較大小西土,而且會(huì)報(bào)錯(cuò)鞍盗。
兩種常見(jiàn)的排序方式。
- 自然排序(實(shí)現(xiàn)Comparable接口):比較兩個(gè)對(duì)象是否相同的標(biāo)準(zhǔn)是:compareTo返回0
- 定制排序(和Comparator相關(guān)):標(biāo)膠兩個(gè)對(duì)象是否相等的標(biāo)準(zhǔn)是:compare返回0
如:
package com.liquan.exer;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
/**
* @auth nan
* @create 2019-12-28 11:21
*/
public class SetDemo1 {
/**
* 在list內(nèi)去除重復(fù)數(shù)字肋乍,要求盡量簡(jiǎn)單
*/
@Test
public void test(){
List list = new ArrayList();
list.add(new Integer(1));
list.add(new Integer(2));
list.add(new Integer(3));
list.add(new Integer(4));
list.add(new Integer(4));
HashSet set = new HashSet();
set.addAll(list);
List list2 = new ArrayList(set);
for (Object integer: list2){
System.out.println(integer);
}
}
}
Set集合使用的綜合案例
- 利用Set的特性去重List
public class SetDemo1 {
@Test
public void test(){
List list = new ArrayList();
list.add(new Integer(1));
list.add(new Integer(2));
list.add(new Integer(3));
list.add(new Integer(4));
list.add(new Integer(4));
HashSet set = new HashSet();
set.addAll(list); // 相同的元素是添加不進(jìn)去的
List list2 = new ArrayList(set);
for (Object integer: list2){
System.out.println(integer);
}
}
}
- 對(duì)于set無(wú)序和不可重復(fù)的加深理解案例
package com.liquan.exer;
import org.junit.Test;
import java.util.HashSet;
public class SetDemo2 {
// 對(duì)于set一點(diǎn)先要equals
@Test
public void test(){
Person p1 = new Person(1, "AA");
Person p2 = new Person(2, "BB");
HashSet set = new HashSet();
set.add(p1);
set.add(p2);
System.out.println(set); // [Person{id=1, name='AA'}, Person{id=2, name='BB'}]
p1.name = "CC";
set.remove(p1);
System.out.println(set); // 在重寫(xiě)hashCode和equals方法的前提下得到的是[Person{id=1, name='CC'}, Person{id=2, name='BB'}]
// 因?yàn)閞emove在移除的時(shí)候判斷存在才移除媒区,判斷存在則需要去找觅闽,更具h(yuǎn)ash值去找對(duì)應(yīng)的位置谱煤,在移除前p1.name是CC,所以會(huì)用CC
// 去得到一個(gè)hash值禽拔,此時(shí)得到的hash值和AA得到hash值不一樣室叉,此時(shí)根據(jù)這個(gè)hash值找到的內(nèi)存地址并沒(méi)有值,所以并沒(méi)有真正意義上的
// 刪除茧痕。所以此時(shí)還是原來(lái)的兩個(gè)值,只不過(guò)AA被換成了CC.
set.add(new Person(1, "CC")); // 同樣能添加成功
System.out.println(set); // [Person{id=1, name='CC'}, Person{id=1, name='CC'}, Person{id=2, name='BB'}]
// 因?yàn)樘砑拥臅r(shí)候按CC去計(jì)算hash值踪旷,得到的hash值得地方并沒(méi)有值存儲(chǔ),因此能添加成功
set.add(new Person(1, "AA")); // 同樣能添加成功
System.out.println(set); // [Person{id=1, name='CC'}, Person{id=1, name='CC'}, Person{id=1, name='AA'}, Person{id=2, name='BB'}]
// 因?yàn)殡m然拿AA得到的hash值的地方有值令野,但是通過(guò)equals的時(shí)候CC和AA不一樣,所以同樣能添加成功气破。
}
}