Set集合
Set接口:Set集合繼承自Collection集合
Set:底層數據結構是一個哈希表,能保證元素是唯一的,元素不重複! 它通過它的子實現了HashSet集合去實例化,HashSet集合底層是HashMap集合的實例!例. 需求:Set集合存儲字符串元素並遍歷
public class SetDemo { public static void main(String[] args) { //創建Set集合對象 Set<String> set = new HashSet<String>() ; //添加元素 set.add("hello"); set.add("java") ; set.add("java") ; set.add("world") ; set.add("world") ; set.add("world") ; //增強for遍歷 for(String s :set){ System.out.println(s); } } }
List集合和Set集合的區別?
List:元素可以重複,有序性(存儲和取出一致)
Set集合的子實現類:
1)HashSet:
實現Set接口,依靠Hashtable,他不保證迭代的順序,特別的是,它不能保證數據保持不變,隨着時間的推移。這個集合可以包含null值。存儲結構實際爲HashMap。
HashSet集合的add()方法,底層是依賴於雙列集合HashMap<K,V>的put(K key,V value)來實現的
put(K key,V value):
底層又依賴於HashCode()和equals()方法,傳遞添加元素的時候,首先判斷的是
每一個元素對應的HashCode值是否一樣,如果HashCode值一樣,還比較他們的equals()方法,由於現在集合存儲的是String類型,String類型本身重寫
了equals()方法,所以,默認比較的是內容是否相同,如果內容相同,這裏最終返回的就是第一次存儲的那個元素,由這兩個方法保證元素唯一性!
例:使用HashSet集合存儲自定義對象並遍歷
import java.util.HashSet;
public class HashSetDemo {
public static void main(String[] args) {
//創建一個HashSet集合對象
HashSet<Student> hs = new HashSet<Student>() ;
//創建學生對象
Student s1 = new Student("高圓圓", 27) ;
Student s2 = new Student("張三", 25) ;
Student s3 = new Student("唐嫣", 26) ;
Student s4 = new Student("鄧超", 29) ;
Student s5 = new Student("胡歌", 23) ;
Student s6 = new Student("高圓圓", 27) ;
//給集合中添加學生對象
hs.add(s1) ;
hs.add(s2) ;
hs.add(s3) ;
hs.add(s4) ;
hs.add(s5) ;
hs.add(s6) ;
//增強for遍歷
for(Student s : hs){
System.out.println(s.getName()+"---"+s.getAge());
}
/**
* "高圓圓---27" 重複出現是因爲現在是自定義對象:在當前自定義對象的類中沒有重寫兩個方法
* hashCode和equals()方法;HashSet底層是依賴於這兩個實現來保證元素的唯一性!
*
* */
}
}
HashSet的子類LinkedHashSet集合:
由哈希表保證元素的唯一性
由鏈接列表來保證元素的有序性!
具有可預知迭代順序的Set接口的哈希表和鏈接列表實現。此實現與HashSet的不同之處在於,後者維護着一個運行於所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,該迭代順序可爲插入順序或是訪問順序。
public class LinkedHashSetDemo {
public static void main(String[] args) {
//創建LinkedHashSet集合對象
LinkedHashSet<String> link = new LinkedHashSet<String>();
//給集合中添加元素
link.add("hello") ;
link.add("world") ;
link.add("world") ;
link.add("Java") ;
link.add("Java") ;
link.add("JavaWeb") ;
link.add("JavaWeb") ;
//遍歷集合
for(String s: link){
System.out.println(s);//元素唯一併且有序
}
}
}
2) TreeSet集合
先了解一下紅黑樹
紅黑樹是一種自平衡的二叉查找樹 ,那麼二叉查找樹(BST)具備什麼特性呢?
1.左子樹上所有結點的值均小於或等於它的根結點的值。
2.右子樹上所有結點的值均大於或等於它的根結點的值。
3.左、右子樹也分別爲二叉排序樹。
這種方式是二分查找的思想,當需要查找10這個元素時,查找的次數就是樹的高度,但是二叉查找樹也存在缺陷,比如
所以紅黑樹應運而生關於紅黑樹的規則:
1.節點是紅色或黑色。
2.根節點是黑色。
3.每個葉子節點都是黑色的空節點(NIL節點)。
4 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
5.從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。
下圖中這棵樹,就是一顆典型的紅黑樹:
紅黑樹可以通過變色 ,旋轉來調整被打破的規則
TreeSet集合底層是依賴於TreeMap的實例,而TreeMap<K,V>是依賴於紅黑樹結構實現的
分兩種:
自然排序:
比較器排序
這種排序的使用取決於開發者是用什麼樣的構造方法
public class TreeSetDemo {
public static void main(String[] args){
// throw new IOException();
//創建TreeSet集合對象
//構造方法:
//public TreeSet():無參構造:根據其元素的自然順序進行排序
//publict TreeSet(Comparaptr<E> com)
TreeSet<Integer> ts = new TreeSet<Integer>();//
//添加元素
//20,18,23,22,17,24,19,18,24
ts.add(20);// Integer i = Integer.valueOf(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遍歷
for(Integer i : ts){
System.out.print(i+ " ");//17 18 19 20 22 23 24 :唯一併且排序:自然排序(升序排序)
}
}
}
TreeSet集合存儲自定義對象並遍歷
對於TreeSet集合存儲自定義對象必須實現一個接口:compareable接口
Student類中實現了compareable接口,重寫了comapreTo()方法,裏面的邏輯是一個排序條件:
public class TreeSetDemo2 {
public static void main(String[] args) {
//創建TreeSet集合對象
TreeSet<Student> ts = new TreeSet<Student>() ;//
//創建學生對象
Student s1 = new Student("linqingxia", 28) ;
Student s2 = new Student("fengqingy", 28) ;
Student s3 = new Student("gaoyuanyuan", 27) ;
Student s4 = new Student("liushishi", 26) ;
Student s5 = new Student("wanglihong", 29) ;
Student s6 = new Student("zhangguorong", 30) ;
Student s7 = new Student("zhangguorong", 30) ;
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
//遍歷
for(Student s : ts){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
需求:按照學生姓名長度從小到大進行排序
元素唯一性:取決返回值是否爲0
要使用TreeSet集合的比較器排序:
依賴於構造方法:
public TreeSet(Comparator<E> comparator)
兩種方式實現比較器排序:
Comparator接口作爲形式參數進行傳遞,需要該接口的子實現類對象
方式1:自定義一個類,類實現Comparator接口,作爲子實現類
方式2:可以使用接口的匿名內部類來實現(開發中,由於減少代碼書寫量,不需要自定義接口的子實現類,直接這種格式! )
public class TreeSetDemo {
public static void main(String[] args) {
//創建TreeSet集合使用比較器進行給元素進行排序
//public TreeSet(Comparator<E> comparator):有參構造
//使用接口的匿名內部類來實現
/**
* 格式
* new 接口名或者類名(){
* 重寫方法() ;
* }
*/
// TreeSet<Student> ts = new TreeSet<Student>(new MyComparator()) ;
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// return 0;
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 ;
}
}) ;
//創建學生對象
Student s1 = new Student("gaoyuanyan", 27) ;
Student s2 = new Student("liushishi", 22);
Student s3 = new Student("fengqingy", 23) ;
Student s4 = new Student("wuqilong", 35) ;
Student s5 = new Student("gaoyuanyuan",27) ;
Student s6 = new Student("zhangguorong",29) ;
Student s7 = new Student("gaoyuanyuan",26) ;
//添加元素
ts.add(s1) ;
ts.add(s2) ;
ts.add(s3) ;
ts.add(s4) ;
ts.add(s5) ;
ts.add(s6) ;
ts.add(s7) ;
//增強for遍歷
for(Student s : ts){
System.out.println(s.getName()+"----"+s.getAge());
}
}
}
鍵盤錄入五個農藥英雄比較他們的各自特點及總體性能public class TreeSetTest {
public static void main(String[] args) {
//創建TreeSet集合
TreeSet<Hreo> ts = new TreeSet<Hreo>(new Comparator< Hreo>() {
@Override
public int compare(Hreo h1, Hreo h2) {
//條件
int num = h2.getSum() - h1.getSum();
int num2 = num == 0?h1.getLive()-h2.getLive():num;
int num3 = num == 0?h1.getAttack()-h2.getAttack():num2;
int num4 = num == 0?h1.getSkill()-h2.getSkill():num3;
int num5 = num == 0?h2.getDifficult()-h1.getDifficult():num4;
return num5;
}
});
System.out.println("錄入英雄信息開始:");
//創建鍵盤錄入並使用String類型接受
for(int x =1;x <= 5;x++) {
Scanner sc = new Scanner(System.in);
System.out.println("請輸入第"+x+"個英雄的姓名:");
String name = sc.nextLine();
System.out.println("請輸入第"+x+"個英雄的生存值:");
String liveString = sc.nextLine();
System.out.println("請輸入第"+x+"個英雄的技能值:");
String skillString = sc.nextLine();
System.out.println("請輸入第"+x+"個英雄的難度值:");
String difficultString = sc.nextLine();
System.out.println("請輸入第"+x+"個英雄的攻擊值:");
String attackString = sc.nextLine();
//創建英雄對象
Hreo h = new Hreo();
//把英雄信息傳進去
h.setName(name);
//將String類型轉爲int類型
h.setLive(Integer.parseInt(liveString));
h.setSkill(Integer.parseInt(skillString));
h.setDifficult(Integer.parseInt(difficultString));
h.setAttack(Integer.parseInt(attackString));
ts.add(h);
System.out.println("英雄信息錄入完畢!");
System.out.println("英雄性能從高到低排列如下:");
System.out.println("姓名\t生存\t技能\t難度\t攻擊");
//增強for遍歷
for(Hreo h1:ts) {
System.out.println(h1.getName() + "\t" + h1.getLive() + "\t" + h1.getSkill() + "\t" +
h1.getDifficult() + "\t" + h1.getAttack());
}
}
}
}
獲取10個1-20之間的隨機數,要求不能重複
public class Test {
public static void main(String[] args) {
//1)創建一個隨機數生成器
Random r = new Random();
//2)創建ArrayList集合,類型Integer
ArrayList<Integer> array = new ArrayList<Integer>() ;
//3)定義統計遍歷
int count = 0 ;
//4)循環判斷
while(count <10){
//通過隨機數生成器獲取:1-20之間的隨機數public int nextInt(int n):生成隨機數的範圍:[0,n)
int number = r.nextInt(20) +1;
//有隨機數了,還需要判斷集合中是否包含這些隨機數
if(!array.contains(number)){
//如果不包含,才添加到集合中
array.add(number) ;
count ++ ;
}
}
//遍歷集合:增強for
for(Integer i : array){
System.out.println(i);
}
}
}