List集合的特色是有序的,可重複的,是否是存在這一種無序,且能保證元素惟一的集合呢?(HashSet )這就涉及到咱們今天所要講的Set集合java
Set能夠理解爲行爲不一樣的Collection數組
Collection安全
List —— 有序(存儲順序和取出順序一致),可重複數據結構
Set—— 無序(存儲順序和取出順序不一致),惟一框架
咱們首先要清楚有序無序,究竟是什麼意思?ide
集合所說的序,是指元素存入集合的順序,當元素存儲順序和取出順序一致時就是有序,不然就是無序。源碼分析
咱們通常說的無序是指HashSet,它既不能保證存儲和取出順序一致,更不能保證天然順序(a-z),而TreeSet 是能夠實現天然順序的。(HashSet的有無序問題但是個大問題,下一篇專篇講解)學習
//添加功能 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):同上,返回的數組的運行時類型是指定數組的運行時類型
//判斷元素是否重複,爲子類提升重寫方法 boolean equals(Object o):將指定的對象與此集合進行比較以實現相等 int hashCode();:返回此集合的哈希碼值
Set集合中的方法用法並不難,能夠參照前面Collection、List集合的講解,對照學習,咱們重點講解Set中一些重要的特色。測試
一句話記住它:一種沒有重複元素的無序集合this
咱們先說說無序是怎麼回事,HashSet 它不保證 set 的迭代順序,特別是它不保證該順序恆久不變,也就是說它的存儲順序和取出順序不一致,雖說它無序,可是,做爲集合來講,它確定有它本身的存儲順序,而你的順序剛好和它的存儲順序一致,這表明不了有序。下一篇專篇講解這一問題!
無序問題因爲篇幅較長,咱們先放到另外一邊
咱們先來思考一下,HashSet是如何保證不重複的呢?
經過查看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才相等
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集合應用時的選擇
是否惟一
若是三原則
若是你知道是List,可是不知道是哪一個List,就用ArrayList。
若是你知道是Collection集合,可是不知道使用誰,就用ArrayList。
若是你知道用集合,就用ArrayList。
在集合中常見的數據結構
ArrayXxx:底層數據結構是數組,查詢快,增刪慢
LinkedXxx:底層數據結構是鏈表,查詢慢,增刪快
HashXxx:底層數據結構是哈希表。依賴兩個方法:hashCode()和equals()
TreeXxx:底層數據結構是二叉樹。兩種方式排序:天然排序和比較器排序
若是內容中有什麼不足,或者錯誤的地方,歡迎你們給我留言提出意見, 蟹蟹你們 !^_^
若是能幫到你的話,那就來關注我吧!(系列文章均會在公衆號第一時間更新)
在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤
一個堅持推送原創Java技術的公衆號:理想二旬不止