Java集合框架——Set接口

第三階段 JAVA常見對象的學習

集合框架——Set接口

List集合的特色是有序的,可重複的,是否是存在這一種無序,且能保證元素惟一的集合呢?(HashSet )這就涉及到咱們今天所要講的Set集合

Set能夠理解爲行爲不一樣的Collectionjava

(一) 概述及功能

(1) 概述

Collection數組

  • List —— 有序(存儲順序和取出順序一致),可重複
  • Set—— 無序(存儲順序和取出順序不一致),惟一

    咱們首先要清楚有序無序,究竟是什麼意思?安全

    集合所說的序,是指元素存入集合的順序,當元素存儲順序和取出順序一致時就是有序,不然就是無序。 數據結構

    咱們通常說的無序是指HashSet,它既不能保證存儲和取出順序一致,更不能保證天然順序(a-z),而TreeSet 是能夠實現天然順序的。(HashSet的有無序問題但是個大問題,下一篇專篇講解)框架

(2) 功能

A:基本功能:(繼承而來)
//添加功能
boolean add(E e):若是指定的元素不存在,則將其指定的元素添加(可選操做) 
boolean addAll(Collection<? extends E> c):將指定集合中的全部元素添加到此集合

//刪除功能
void clear():移除集合中的全部元素
boolean remove(Object o):從集合中移除指定的元素
boolean removeAll(Collection<?> c):從集合中移除一個指定的集合元素(有一個就返回true)

//長度功能
int size();

//判斷功能
boolean isEmpty():判斷集合是否爲空
boolean contains(Object o):判斷集合中是否包含指定元素
boolean containsAll(Collection<?> c):判斷集合中是否包含指定的一個集合中的元素
boolean retainAll(Collection<?> c):僅保留該集合中包含在指定集合中的元素

//獲取Set集合的迭代器:
Iterator<E> iterator();

//把集合轉換成數組
Object[] toArray():返回一個包含此集合中全部元素的數組
<T> T[] toArray(T[] a):同上,返回的數組的運行時類型是指定數組的運行時類型
B:特有功能:
//判斷元素是否重複,爲子類提升重寫方法
boolean equals(Object o):將指定的對象與此集合進行比較以實現相等
int hashCode();:返回此集合的哈希碼值

Set集合中的方法用法並不難,能夠參照前面Collection、List集合的講解,對照學習,咱們重點講解Set中一些重要的特色。ide

(二) HashSet

一句話記住它:一種沒有重複元素的無序集合源碼分析

咱們先說說無序是怎麼回事,HashSet 它不保證 set 的迭代順序,特別是它不保證該順序恆久不變,也就是說它的存儲順序和取出順序不一致,雖說它無序,可是,做爲集合來講,它確定有它本身的存儲順序,而你的順序剛好和它的存儲順序一致,這表明不了有序。下一篇專篇講解這一問題!學習

無序問題因爲篇幅較長,咱們先放到另外一邊測試

咱們先來思考一下,HashSet是如何保證不重複的呢? this

經過查看HashSet中add方法的源碼

// HashSet 源碼節選-JKD8
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

咱們能夠看到HashSet中add方法所調用的是HashMap中的put方法,咱們定位過去

因爲解釋篇幅較長,直接給出結論,具體源碼解釋在HashMap源碼分析中具體講解

這個方法底層主要依賴 兩個方法:hashCode()和equals()。

  • 步驟:
  • HashSet方法調用add方法時,調用hashCode(),獲得一個哈希值,判斷哈希值是否相同。
  • 相同:執行equals()方法

    ​ 返回true:說明元素重複,就不添加

    ​ 返回false:說明元素不重複,就添加到集合

  • 不一樣:就直接把元素添加到集合

如今你們可能想問一句,只使用hashCode()來判斷是否重複能夠嗎?答案是否認的

咱們給出這樣一句話:

對象相等則hashCode必定相等,hashCode相等對象未必相等,只有equals返回true,hashCode才相等

  • 若是類沒有重寫這兩個方法,默認使用的Object()。通常來講不會相同。
  • 而String類重寫了hashCode()和equals()方法,因此,它就能夠把內容相同的字符串去掉。只留下一個。
  • 對於String 類型來講,不用重寫 hashCode()方法和equals()方法均可以保證元素的惟一性,可是若是不是Stirng,而是其它自定義的對象就要重寫這兩個方法才能保證元素的惟一性。

(三) TreeSet

概述:

TreeSet:底層是二叉樹結構(紅黑樹是一種自平衡的二叉樹)

如何存儲

那麼這一種結構又是如何存儲元素的呢?(咱們將上圖中圓圈稱爲節點)

  • 第一個元素存儲的時候,直接做爲根節點存儲
  • 第二個元素開始,每一個元素從根節點開始比較

    • 若大 則做爲右孩子
    • 若小 則做爲左孩子
    • 相等 則不做處理

咱們來舉一個例子看看:

import java.util.Set;
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        Set<Integer> ts = new TreeSet<Integer>();
        ts.add(20);
        ts.add(18);
        ts.add(23);
        ts.add(22);
        ts.add(17);
        ts.add(24);
        ts.add(19);
        ts.add(18);
        ts.add(24);

        for (Integer i : ts) {
            System.out.print(i + " ");
        }
    }
}

//運行結果
17 18 19 20 22 23 24

咱們使用圖片來解釋一下上面的代碼

咱們將第一個數字20 做爲根節點存放,第二個數字18比20小因此放在左邊 23大放在右邊

例如22這個數字是如何放到如圖的位置呢?

首先22先和20比較是大的因此放到右邊,接着繼續和23進行比較是小的,因此放到23的左邊,接下來同理

咱們看到運行結果,很神奇的是按照順序輸出的,這也正符合了咱們一開始給出的結論:TreeSet 是能夠實現天然順序的

如何取出

那麼TreeSet中元素是如何取出來的呢?

從根節點開始,按照左,中,右的原則依次取出元素便可

分析:咱們的根節點是20,因此先看左邊也就是18,可是下面還有子節點,咱們繼續看左邊因此第一個數字就是17,而後再看中和右也就是18和19,這時候根節點的左邊也就所有看完了,因此接着就是中間的根節點20,右邊同理。

如何存儲自定義對象

咱們設定一種場景,存儲學生類中的學生對象,而且按照年齡從小到大排序(天然排序)

當知足全部成員變量的值都相同的時候即爲同一個元素

注意:若是一個類的元素要想可以進行天然排序,就必須實現天然排序接口(關鍵)

public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student s) {
        //按照年齡排序
        //年齡相同的時候,去看姓名是否也相同
        //String 默認實現了Comparavle接口,因此能夠直接使用字符串的compareTo方法
        int num = this.age - s.age;
        int num2 = num == 0 ? this.name.compareTo(s.name) : num;
        return num2;
    }
}
import java.util.Set;
import java.util.TreeSet;

public class StudentDemo {
    public static void main(String[] args) {
        Set<Student> ts = new TreeSet<Student>();
        Student s1 = new Student("張三",27);
        Student s2 = new Student("李四",16);
        Student s3 = new Student("王五",40);
        Student s4 = new Student("馬六",40);
        Student s5 = new Student("馬六",40);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);

        for (Student s : ts){
            System.out.println(s);
        }

    }
}

//運行結果
Student{name='李四', age=16}
Student{name='張三', age=27}
Student{name='王五', age=40}
Student{name='馬六', age=40}

咱們能夠專門定義了一個類MyComparator,其實也能夠省略這一個類,直接在TreeSetDemo測試類中定義一個匿名內部類

package cn.bwh_04_TreeSet;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class StudentDemo2 {
    public static void main(String[] args) {
        Set<Student2> ts = new TreeSet<Student2>(new Comparator<Student2>() {
            @Override
            public int compare(Student2 s1, Student2 s2) {
                //姓名長度
                int num = s1.getName().length() - s2.getName().length();
                //姓名內容
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                //年齡
                int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
                return num3;
            }
        });
        Student2 s1 = new Student2("張三", 27);
        Student2 s2 = new Student2("李四", 16);
        Student2 s3 = new Student2("王五", 40);
        Student2 s4 = new Student2("馬六", 40);
        Student2 s5 = new Student2("馬六", 40);

        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);

        for (Student2 s : ts) {
            System.out.println(s);
        }
    }
}

Collection 集合總結

Collection集合應用時的選擇

是否惟一

  • 惟一:Set

    • 須要排序:TreeSet
    • 不須要排序:HashSet
    • 若是你知道是Set,可是不知道是哪一個Set,就用HashSet。
  • 不惟一:List

    • 須要安全:Vector
    • 不須要安全:ArrayList或者LinkedList
    • 查詢多:ArrayList
    • 增刪多:LinkedList

若是三原則

若是你知道是List,可是不知道是哪一個List,就用ArrayList。

若是你知道是Collection集合,可是不知道使用誰,就用ArrayList。

若是你知道用集合,就用ArrayList。

在集合中常見的數據結構

ArrayXxx:底層數據結構是數組,查詢快,增刪慢

LinkedXxx:底層數據結構是鏈表,查詢慢,增刪快

HashXxx:底層數據結構是哈希表。依賴兩個方法:hashCode()和equals()

TreeXxx:底層數據結構是二叉樹。兩種方式排序:天然排序和比較器排序

結尾:

若是內容中有什麼不足,或者錯誤的地方,歡迎你們給我留言提出意見, 蟹蟹你們 !^_^

若是能幫到你的話,那就來關注我吧!(系列文章均會在公衆號第一時間更新)

在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤

一個堅持推送原創Java技術的公衆號:理想二旬不止

相關文章
相關標籤/搜索