java 之集合體系(三 set)
1.set集合
set中的方法和collection中的方法是一樣的,沒有任何特有方法;主要學習set的子類:HashSet,TreeSet
2.HashSet
特點:無索引,元素不重複(唯一),無序(存取順序不一致)
public static void demo1() { HashSet<String> hs = new HashSet<>(); //建立HashSet物件 boolean b1 = hs.add("a"); boolean b2 = hs.add("a"); //當向set集合中儲存重複元素的時候返回為false hs.add("b"); hs.add("c"); hs.add("d"); System.out.println(hs); //HashSet的繼承體系中有重寫toString方法 System.out.println(b1); System.out.println(b2); for (String string : hs) { //只要能用迭代器迭代的,就可以使用增強for迴圈遍歷 System.out.println(string); } }
對於基本資料型別的話是可以保證唯一性的,但是物件資料型別的話,存的是引用地址;都是不一樣的;我們任務物件的某些屬性一樣的話就認為這是相同資料;
解決方案:重寫複雜資料型別的equals方法和hastCode方法;
/*@Override public boolean equals(Object obj) { System.out.println("執行了嗎"); Person p = (Person)obj; return this.name.equals(p.name) && this.age == p.age; } @Override public int hashCode() { final int NUM = 38; return name.hashCode() * NUM + age; }*/
這裡和hash演算法有關;會根據你存入資料的地址算出一個索引,索引就是儲存的門牌號(所以是無序的,算出來的值是無序的);只有當索引相同的時候才會呼叫equals方法進行比較;其實在開放工具軟體中是可以自己生成類的equals和hashCode方法,所以這個兩個方法很重要的;
/* * 為什麼是31? * 1,31是一個質數,質數是能被1和自己本身整除的數 * 2,31這個數既不大也不小 * 3,31這個數好算,2的五次方-1,2向左移動5位 */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) //呼叫的物件和傳入的物件是同一個物件 return true; //直接返回true if (obj == null) //傳入的物件為null return false; //返回false if (getClass() != obj.getClass()) //判斷兩個物件對應的位元組碼檔案是否是同一個位元組碼 return false; //如果不是直接返回false Person other = (Person) obj; //向下轉型 if (age != other.age) //呼叫物件的年齡不等於傳入物件的年齡 return false; //返回false if (name == null) { //呼叫物件的姓名為null if (other.name != null) //傳入物件的姓名不為null return false; //返回false } else if (!name.equals(other.name)) //呼叫物件的姓名不等於傳入物件的姓名 return false; //返回false return true; //返回true }
* 1.HashSet原理
* 我們使用Set集合都是需要去掉重複元素的, 如果在儲存的時候逐個equals()比較, 效率較低,雜湊演算法提高了去重複的效率, 降低了使用equals()方法的次數
* 當HashSet呼叫add()方法儲存物件的時候, 先呼叫物件的hashCode()方法得到一個雜湊值, 然後在集合中查詢是否有雜湊值相同的物件
* 如果沒有雜湊值相同的物件就直接存入集合
* 如果有雜湊值相同的物件, 就和雜湊值相同的物件逐個進行equals()比較,比較結果為false就存入, true則不存
* 2.將自定義類的物件存入HashSet去重複
* 類中必須重寫hashCode()和equals()方法
* hashCode(): 屬性相同的物件返回值必須相同, 屬性不同的返回值儘量不同(提高效率)
* equals(): 屬性相同返回true, 屬性不同返回false,返回false的時候儲存
LinkedHashSet是hashset的子類:底層使用連結串列實現的:
特點:連結串列,元素唯一不重複,存取有順序(怎麼存就怎麼取,set集合中唯一一個)
做一個案例:編寫一個程式,獲取10個1至20的隨機數,要求隨機數不能重複;不重複所以這裡hashset和linkedhashset都可以使用;
HashSet<Integer> hs = new HashSet<>(); //建立集合物件
Random r = new Random(); //建立隨機數物件
while(hs.size() < 10) {
int num = r.nextInt(20) + 1; //生成1到20的隨機數
hs.add(num);
}
for (Integer integer : hs) { //遍歷集合
System.out.println(integer); //列印每一個元素
}
將集合中的重複元素去掉:
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("a");
list.add("a");
list.add("b");
list.add("b");
list.add("b");
list.add("b");
list.add("c");
list.add("c");
list.add("c");
list.add("c");
System.out.println(list);
System.out.println("去除重複後:");
getSingle(list);
System.out.println(list);
}
/*
* 將集合中的重複元素去掉
* 1,void
* 2,List<String> list
*/
public static void getSingle(List<String> list) {
LinkedHashSet<String> lhs = new LinkedHashSet<>();
lhs.addAll(list); //將list集合中的所有元素新增到lhs
list.clear(); //清空原集合
list.addAll(lhs); //將去除重複的元素添回到list中
}
之前使用建立一個新的ArrayList,使用contain方法判斷是否包含元素,來判斷是否重複;然後存入的;上面直接使用了set集合,更方便;3.TreeSet(底層是二叉樹資料結構實現)
特點:set的共性元素唯一,元素排序 基本資料型別,treeset是可以自己實現排序的,但是你如果放入複雜資料型別的話就需要程式設計師自己制定排序的方式;就和元素唯一性去重複是一樣的,基本資料型別系統是可以的,當複雜的時候系統根據物件的地址值去判斷是否唯一,顯然都是不重複的,所以需要重寫相應的方法; 這裡要讓存入物件實現一個介面comparable介面實現compareTo方法;資料的儲存方式和compareto的返回結果有關; compareTo方法中返回一個0時:集合中已經有一個相同的元素;返回0表示每次存入的資料是一樣的 返回一個正數的時候:不排序,但是怎麼存就怎麼取;返回正數表示存在二叉樹的右邊; 返回一個負數的時候:不排序,但是是倒序的取出;返回負數表示存在二叉樹的左邊; ps:字串中的compareTo比較的是對應碼錶中的值; /*@Override
//按照年齡排序
public int compareTo(Person o) {
int num = this.age - o.age; //年齡是比較的主要條件
return num == 0 ? this.name.compareTo(o.name) : num;//姓名是比較的次要條件
}*/
/*@Override
//按照姓名排序
public int compareTo(Person o) {
int num = this.name.compareTo(o.name); //姓名是主要條件
return num == 0 ? this.age - o.age : num; //年齡是次要條件
}*/
通過比較器來排序:
實現compartor介面;
class CompareByLen /*extends Object*/ implements Comparator<String> {
@Override
public int compare(String s1, String s2) { //按照字串的長度比較
int num = s1.length() - s2.length(); //長度為主要條件
return num == 0 ? s1.compareTo(s2) : num; //內容為次要條件
}
}
//需求:將字串按照長度排序
TreeSet<String> ts = new TreeSet<>(new CompareByLen()); //Comparator c = new CompareByLen();
ts.add("aaaaaaaa");
ts.add("z");
ts.add("wc");
ts.add("nba");
ts.add("cba");
System.out.println(ts);
Treeset總結:
* 1.特點
* TreeSet是用來排序的, 可以指定一個順序, 物件存入之後會按照指定的順序排列
* 2.使用方式
* a.自然順序(Comparable)
* TreeSet類的add()方法中會把存入的物件提升為Comparable型別
* 呼叫物件的compareTo()方法和集合中的物件比較
* 根據compareTo()方法返回的結果進行儲存
* b.比較器順序(Comparator)
* 建立TreeSet的時候可以制定 一個Comparator
* 如果傳入了Comparator的子類物件, 那麼TreeSet就會按照比較器中的順序排序
* add()方法內部會自動呼叫Comparator介面中compare()方法排序
* 呼叫的物件是compare方法的第一個引數,集合中的物件是compare方法的第二個引數
* c.兩種方式的區別
* TreeSet建構函式什麼都不傳, 預設按照類中Comparable的順序(沒有就報錯ClassCastException)
* TreeSet如果傳入Comparator, 就優先按照Comparator
案例:
* 在一個集合中儲存了無序並且重複的字串,定義一個方法,讓其有序(字典順序),而且還不能去除重複
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("ccc");
list.add("ccc");
list.add("aaa");
list.add("aaa");
list.add("bbb");
list.add("ddd");
list.add("ddd");
sort(list);
System.out.println(list);
}
/*
* 對集合中的元素排序,並保留重複
* 1,void
* 2,List<String> list
*/
public static void sort(List<String> list) {
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {//定義比較器(new Comparator(){}是Comparator的子類物件)
@Override
public int compare(String s1, String s2) { //重寫compare方法
int num = s1.compareTo(s2);
return num == 0 ? 1 : num; //比較內容如果內容一樣返回一個不為0的數字即可
}
});
ts.addAll(list); //將list集合中的所有元素新增到ts中
list.clear(); //清空list
list.addAll(ts); //將ts中排序並保留重複的結果在新增到list中
}
總結
* 1.List* a.普通for迴圈, 使用get()逐個獲取
* b.呼叫iterator()方法得到Iterator, 使用hasNext()和next()方法
* c.增強for迴圈, 只要可以使用Iterator的類都可以用
* d.Vector集合可以使用Enumeration的hasMoreElements()和nextElement()方法 * 普通for迴圈,迭代器,增強for迴圈是否可以在遍歷的過程中刪除
* 2.Set(沒有索引,不能獲取指定 i 的元素)
* a.呼叫iterator()方法得到Iterator, 使用hasNext()和next()方法
* b.增強for迴圈, 只要可以使用Iterator的類都可以用