集合之Set接口帆锋,Map接口

1.Set接口

Set接口是通過元素的equals方法顷啼,來判斷是否為重復(fù)元素。

Set接口常用的實現(xiàn)類有:

  • HashSet類
  • LinkedHashSet類
  • TreeSet類

Set接口的特點

  • 唯一
  • 無序

1.1HashSet類

1.1.1介紹
  • HashSet類實現(xiàn)了Set接口
  • 存儲到HashSet中的對象必須是唯一的嫡秕,不能重復(fù)
  • 對象唯一性的判斷依賴于hashCode()與equals()方法
  • HashSet集合不能保證的迭代順序與元素存儲順序相同
1.1.2常用方法
Modifier and Type Method and Description
boolean add(E e) 將指定的元素添加到此集合(如果尚未存在)。
void clear() 從此集合中刪除所有元素苹威。
boolean contains(Object o) 如果此集合包含指定的元素昆咽,則返回 true
boolean isEmpty() 如果此集合不包含元素牙甫,則返回 true 掷酗。
Iterator<E> iterator() 返回此集合中元素的迭代器。
boolean remove(Object o) 如果存在窟哺,則從該集合中刪除指定的元素泻轰。
int size() 返回此集合中的元素數(shù)(其基數(shù))。
1.1.3代碼演示
public static void main(String[] args) {
    //1:創(chuàng)建集合且轨,向集合中添加5個對象
    HashSet<String> set = new HashSet<>();
    set.add("林沖");
    set.add("李逵");
    set.add("燕青");
    set.add("武松");//重復(fù)的
    set.add("武松");//重復(fù)的
    
    //2:迭代集合 Set集合沒有g(shù)et()方法糕殉,獲取元素只能通過迭代
    Iterator<String> iterator = set.iterator();
    while (iterator.hasNext()){
        System.out.println(iterator.next());
    }
}

運行結(jié)果

燕青
武松
林沖
李逵

集合中只存入了四個元素亩鬼,重復(fù)的元素武松只存入了一個殖告。

另外也發(fā)現(xiàn)HashSet對元素輸出的順序與輸入的順序不一致阿蝶。

1.1.4底層數(shù)據(jù)結(jié)構(gòu)

HashSet底層是使用哈希表存儲數(shù)據(jù)的,什么是哈希表呢黄绩?

  • 哈希表(Hashtable)羡洁,也叫散列表,是通過key訪問value的一種數(shù)據(jù)結(jié)構(gòu)爽丹,與數(shù)組不同的是數(shù)組通過index訪問value筑煮。
  • 哈希表最大的優(yōu)勢在于查找速度快≡列快速查找的原理是key通過散列函數(shù)(也叫哈希函數(shù))能夠快速定位value存儲的位置真仲。
  • 哈希表底層是數(shù)組+鏈表的數(shù)據(jù)結(jié)構(gòu),哈希表數(shù)據(jù)結(jié)構(gòu)如下圖所示:
1659109825005.png
  • 哈希表中數(shù)組元素不存儲添加到哈希表中的元素初澎,數(shù)組元素存放的是鏈表秸应。

  • 添加到哈希表中的元素存儲在鏈表的節(jié)點上。

  • 當(dāng)向哈希表中存放元素時碑宴,需要根據(jù)元素的特有的數(shù)據(jù)結(jié)合相應(yīng)的算法软啼,計算出元素應(yīng)該放到哪個數(shù)組元素的鏈表上。

  • 這個算法其實就是Object類中的hashCode方法延柠。由于任何對象都是Object類的子類祸挪,所以任何對象有擁有這個方法。也就是在給哈希表中存放對象時贞间,會調(diào)用對象的hashCode方法贿条,算出對象在表中的存放位置,這里需要注意增热,如果兩個對象hashCode方法算出結(jié)果一樣整以,這樣現(xiàn)象稱為哈希沖突,這時會調(diào)用對象的equals方法钓葫,比較這兩個對象是不是同一個對象悄蕾,如果equals方法返回的是true,那么就不會把第二個對象存放在哈希表中础浮,如果返回的是false帆调,就會把這個值存放在哈希表中。

  • 總結(jié):保證HashSet集合元素的唯一豆同,其實就是根據(jù)對象的hashCode和equals方法來決定的番刊。如果我們往集合中存放自定義的對象,那么保證其唯一影锈,就必須復(fù)寫hashCode和equals方法建立屬于當(dāng)前對象的比較方式芹务。

1.1.5代碼演示:HashSet中存儲自定義類型的元素
class Student{
    private String name;
    private int math;
    private int cn;
    public Student(String name, int math, int cn) {
        super();
        this.name = name;
        this.math = math;
        this.cn = cn;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", math=" + math +
                ", cn=" + cn +
                '}';
    }
    //省略 get/set方法
}
public class Demo {
    public static void main(String[] args) {

        HashSet<Student> set = new HashSet<>();
        Student s1 = new Student("林沖", 90, 90);
        Student s2 = new Student("李逵", 92, 60);
        Student s3 = new Student("燕青", 71, 70);
        Student s4 = new Student("時遷", 80, 80);//重復(fù)對象
        Student s5 = new Student("時遷", 80, 80);//重復(fù)對象
        set.add(s1);
        set.add(s2);
        set.add(s3);
        set.add(s4);
        set.add(s5);

        Iterator<Student> iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        System.out.println("s4 哈希碼="+s4.hashCode());
        System.out.println("s5 哈希碼="+s5.hashCode());
    }
}

運行結(jié)果

Student{name='燕青', math=71, cn=70}
Student{name='時遷', math=80, cn=80}
Student{name='李逵', math=92, cn=60}
Student{name='時遷', math=80, cn=80}
Student{name='林沖', math=90, cn=90}
s4 哈希碼=325040804
s5 哈希碼=1173230247

2.Map接口

2.1.1介紹

Map接口下的集合與Collection接口下的集合蝉绷,它們存儲數(shù)據(jù)的形式不同,如下圖枣抱。

  • Collection中的集合熔吗,元素是孤立存在的(理解為單身),向集合中存儲元素采用一個個元素的方式存儲佳晶。

  • Map中的集合桅狠,元素是成對存在的(理解為夫妻)。每個元素由鍵與值(key - value)兩部分組成轿秧,通過鍵可以找對所對應(yīng)的值中跌。

  • Collection中的集合稱為單列集合,Map中的集合稱為雙列集合菇篡。

  • 需要注意的是漩符,Map中的集合不能包含重復(fù)的鍵,值可以重復(fù)驱还;每個鍵只能對應(yīng)一個值嗜暴。

  • Map中常用的集合為HashMap集合、LinkedHashMap集合铝侵。
    Map接口有多個子類灼伤,這里我們主要講解常用的HashMap集合、LinkedHashMap集合咪鲜、Properties集合狐赡。

  • HashMap<K,V>:存儲數(shù)據(jù)采用的哈希表結(jié)構(gòu),元素的存取順序不能保證一致疟丙。由于要保證鍵的唯一颖侄、不重復(fù),需要重寫鍵的hashCode()方法享郊、equals()方法览祖。

  • LinkedHashMap<K,V>:HashMap下有個子類LinkedHashMap,存儲數(shù)據(jù)采用的哈希表結(jié)構(gòu)+鏈表結(jié)構(gòu)炊琉。通過鏈表結(jié)構(gòu)可以保證元素的存取順序一致展蒂;通過哈希表結(jié)構(gòu)可以保證的鍵的唯一、不重復(fù)苔咪,需要重寫鍵的hashCode()方法锰悼、equals()方法。

  • 注意:Map接口中的集合都有兩個泛型變量<K,V>,在使用時团赏,要為兩個泛型變量賦予數(shù)據(jù)類型箕般。

2.1.2常用方法

Modifier and Type Method and Description
void clear() 從這張地圖中刪除所有的映射。
Set<Map.Entry<K,V>> entrySet() 返回此地圖中包含的映射的[Set]視圖舔清。
V get(Object key) 返回到指定鍵所映射的值丝里,或 null如果此映射包含該鍵的映射曲初。
boolean isEmpty() 如果此地圖不包含鍵值映射,則返回 true 杯聚。
Set<K> keySet() 返回此地圖中包含的鍵的[Set]視圖臼婆。
V put(K key, V value) 將指定的值與此映射中的指定鍵相關(guān)聯(lián)。
V remove(Object key) 從該地圖中刪除指定鍵的映射(如果存在)械媒。
int size() 返回此地圖中鍵值映射的數(shù)量目锭。
Collection<V> values() 返回此地圖中包含的值的[Collection]視圖。
  • put方法:將指定的鍵與值對應(yīng)起來纷捞,并添加到集合中
    • 方法返回值為鍵所對應(yīng)的值
      • 使用put方法時,若指定的鍵(key)在鍵集中不存在被去,那么對應(yīng)的值也不存在主儡,返回null,并把指定的鍵值添加到集合中
      • 使用put方法時惨缆,若指定的鍵(key)在鍵集中存在糜值,則返回值為集合中鍵對應(yīng)的值(該值為替換前的值),并把指定鍵所對應(yīng)的值坯墨,替換成指定的新值寂汇。
  • get方法:獲取指定鍵(key)所對應(yīng)的值(value)
  • remove方法:根據(jù)指定的鍵(key)刪除元素,返回被刪除元素的值(value)

2.1.3Map集合的遍歷

  • Map集合遍歷方式是鍵找值:即通過元素中的鍵捣染,獲取鍵所對應(yīng)的值骄瓣。
  • Map集合的鍵集是一個Set集合
  • 遍歷Set集合,分別取出key
  • 通過key找到value
public static void main(String[] args) {
    Map<String, String> map = new HashMap<String,String>();

    map.put("豹子頭", "林沖");
    map.put("黑旋風(fēng)", "李逵");
    map.put("浪子","燕青");
    map.put("行者","武松");

    Set<String> sets = map.keySet();
    Iterator<String> iterator = sets.iterator();
    while (iterator.hasNext()){
        String key = iterator.next();
        String value = map.get(key);
        System.out.println("key="+key +",value="+value);
    }
}

運行結(jié)果

key=豹子頭,value=林沖
key=行者,value=武松
key=黑旋風(fēng),value=李逵
key=浪子,value=燕青

2.1.4Entry鍵值對對象

Map集合除了通過keySet()迭代集合外耍攘,還可以通過entrySet()迭代集合榕栏。

在Map類設(shè)計時,提供了一個嵌套接口:Entry蕾各。Entry將鍵值對的對應(yīng)關(guān)系封裝成了對象扒磁。即鍵值對對象,這樣我們在遍歷Map集合時式曲,就可以從每一個鍵值對(Entry)對象中獲取對應(yīng)的鍵與對應(yīng)的值妨托。

entrySet()方法:用于返回Map集合中所有的鍵值對(Entry)對象,以Set集合形式返回吝羞。

遍歷set集合得到 Entry<String , String> 對象

Entry<String , String> 對象調(diào)用 getKey()得到 key

Entry<String , String> 對象調(diào)用 getValue()得到 value

public static void main(String[] args) {
    Map<String, String> map = new HashMap<String,String>();

    map.put("豹子頭", "林沖");
    map.put("黑旋風(fēng)", "李逵");
    map.put("浪子","燕青");
    map.put("行者","武松");

    Set<Map.Entry<String, String>> entrySet = map.entrySet();
    Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
    while (iterator.hasNext()){
        Map.Entry<String, String> entry = iterator.next();
        String key = entry.getKey();
        String value = entry.getValue();
        System.out.println("key="+key +",value="+value);
    }
}

運行結(jié)果

key=豹子頭,value=林沖
key=行者,value=武松
key=黑旋風(fēng),value=李逵
key=浪子,value=燕青

2.2.1HashMap類

需求:學(xué)生有姓名兰伤、年齡的屬性,學(xué)生都有自己的家庭住址脆贵。將學(xué)生對象和家庭住址存儲到map集合中医清。學(xué)生作為鍵, 家庭住址作為值。

注意卖氨,學(xué)生姓名相同并且年齡相同視為同一名學(xué)生会烙。

class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //重寫equals方法
    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if(!(o instanceof Student)){
            System.out.println("類型錯誤");
            return false;
        }
        Student other = (Student) o;
        return  this.age == other.age && this.name.equals(other.name);

    }
    //重寫hashCode方法
    @Override
    public int hashCode() {
        final int base = 31;
        int result = 1;
        result = base * result + age;
        result = base * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    //重寫 toString 方法
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
    //省略了 get/set 方法
}
/**
 * HashMap集合的用法
 */
public class Demo {
    public static void main(String[] args) {
        //1:創(chuàng)建Map集合對象
        Map<Student, String> map = new HashMap<Student, String>();

        //2:創(chuàng)建學(xué)生對象
        Student s1 = new Student("林沖", 30);
        Student s2 = new Student("李逵", 32);
        Student s3 = new Student("燕青", 22);
        Student s4 = new Student("武松", 29);//重復(fù)對象
        Student s5 = new Student("武松", 29);//重復(fù)對象

        //3:添加到map集合中 學(xué)生對象作為key负懦,家庭地址作為value
        map.put(s1, "河南開封");
        map.put(s2, "山東沂水");
        map.put(s3, "北京大名府");
        map.put(s4, "河北清河");
        map.put(s5, "遼寧鐵嶺");

        //3:取出元素,鍵值對方式
        Set<Map.Entry<Student, String>> entrySet = map.entrySet();
        Iterator<Map.Entry<Student, String>> iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Map.Entry<Student, String> entry = iterator.next();
            Student key = entry.getKey();
            String value = entry.getValue();
            System.out.println("key=" + key + ",value=" + value);
        }
        System.out.println("-------------------- 分界線 ----------------------");
        //3:取出元素柏腻,鍵找值方式
        Set<Student> sets = map.keySet();
        Iterator<Student> iterator1 = sets.iterator();
        while (iterator1.hasNext()) {
            Student key = iterator1.next();
            String value = map.get(key);
            System.out.println("key=" + key + ",value=" + value);
        }
    }
}

運行結(jié)果

key=Student{name='武松', age='29'},value=遼寧鐵嶺
key=Student{name='林沖', age='30'},value=河南開封
key=Student{name='李逵', age='32'},value=山東沂水
key=Student{name='燕青', age='22'},value=北京大名府
-------------------- 分界線 ----------------------
key=Student{name='武松', age='29'},value=遼寧鐵嶺
key=Student{name='林沖', age='30'},value=河南開封
key=Student{name='李逵', age='32'},value=山東沂水
key=Student{name='燕青', age='22'},value=北京大名府

2.2.2底層數(shù)據(jù)結(jié)構(gòu)

HashMap底層數(shù)據(jù)結(jié)構(gòu)是( 數(shù)組 + 鏈表 | 紅黑樹)

默認情況下纸厉,當(dāng)節(jié)點數(shù)量不超過8個時,使用鏈表五嫂,當(dāng)節(jié)點數(shù)量超過8個時使用紅黑樹颗品。這樣設(shè)計是為了提高查詢效率,因為當(dāng)節(jié)點數(shù)量多時沃缘,紅黑樹的查詢效率高于鏈表躯枢。
1659179248735.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市槐臀,隨后出現(xiàn)的幾起案子锄蹂,更是在濱河造成了極大的恐慌,老刑警劉巖水慨,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件得糜,死亡現(xiàn)場離奇詭異,居然都是意外死亡晰洒,警方通過查閱死者的電腦和手機朝抖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谍珊,“玉大人治宣,你說我怎么就攤上這事√浚” “怎么了炼七?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長布持。 經(jīng)常有香客問我豌拙,道長,這世上最難降的妖魔是什么题暖? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任按傅,我火速辦了婚禮,結(jié)果婚禮上胧卤,老公的妹妹穿的比我還像新娘唯绍。我一直安慰自己,他們只是感情好枝誊,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布况芒。 她就那樣靜靜地躺著,像睡著了一般叶撒。 火紅的嫁衣襯著肌膚如雪绝骚。 梳的紋絲不亂的頭發(fā)上耐版,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天,我揣著相機與錄音压汪,去河邊找鬼粪牲。 笑死,一個胖子當(dāng)著我的面吹牛止剖,可吹牛的內(nèi)容都是我干的腺阳。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼穿香,長吁一口氣:“原來是場噩夢啊……” “哼亭引!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扔水,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤痛侍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后魔市,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡赵哲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年待德,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枫夺。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡将宪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出橡庞,到底是詐尸還是另有隱情较坛,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布扒最,位于F島的核電站丑勤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吧趣。R本人自食惡果不足惜法竞,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望强挫。 院中可真熱鬧岔霸,春花似錦、人聲如沸俯渤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽八匠。三九已至絮爷,卻和暖如春趴酣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背略水。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工价卤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渊涝。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓慎璧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親跨释。 傳聞我的和親對象是個殘疾皇子胸私,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容