一. ArrayList嵌套
-
定義
- 在集合中存放集合,和二維數(shù)組類似
-
演示
public static void main(String[] args) { //集合中的元素還是集合 ArrayList<ArrayList<Student>> school = new ArrayList<>(); ArrayList<Student> clas1 = new ArrayList<>(); clas1.add(new Student("小紅",18)); clas1.add(new Student("小明", 20)); school.add(clas1); ArrayList<Student> clas2 = new ArrayList<>(); clas2.add(new Student("小張",18)); clas2.add(new Student("小李", 20)); school.add(clas2); ArrayList<Student> clas3 = new ArrayList<>(); clas3.add(new Student("tom",18)); clas3.add(new Student("jack", 20)); school.add(clas3); System.out.println(school); }
-
測試題
- 需求: 先有一家賓館,賓館中有多個房間, 每個房間有數(shù)量不等客人, 使用集合嵌套的形式模擬并打印所有人的信息
二. Set集合
-
定義
- Set集合存數(shù)的元素是無序的, 而且不允許儲存重復(fù)的元素, 每當(dāng)有新的元素存入的時(shí)候,Set集合會先去過濾, 如果發(fā)現(xiàn)和集合中現(xiàn)有元素出現(xiàn)重復(fù), 就不在允許添加
-
應(yīng)用場景
- 當(dāng)我們不想讓集合中出現(xiàn)重復(fù)的元素時(shí),使用Set集合
- 當(dāng)我們需要排除重復(fù)數(shù)據(jù)時(shí),使用Set集合
-
演示
public static void main(String[] args) { //集合中的元素還是集合 HashSet<Student> s = new HashSet<>(); Student student = new Student("小紅", 18); Student student2 = new Student("小紅", 18); s.add(student); s.add(student2); Iterator<Student> it = s.iterator(); while (it.hasNext()) { Student student3 = it.next(); System.out.println(student3); } }
三. HashSet
-
定義
- HashSet集合中的元素是通過hash值來比較是否相同
- 集合通過元素的hashCode和equals方法來比較兩個元素是否相同, 不同就存入, 相同不存入
- 元素存入的位置未知,和存入的順序無關(guān)
-
存儲原理
- HashSet最后還是存入數(shù)組中, 只是根據(jù)元素的Hash值來確定存入的角標(biāo)位置
- 元素的hash值 ^ (元素的hash值 >>> 16) & (數(shù)組的長度-1)
- HashSet中不是直接存入我們給定的元素, 而是用集合中的一個內(nèi)部類封裝我們存入的元素,所以當(dāng)我們存入null的時(shí)候,不會因?yàn)楹蛿?shù)組中角標(biāo)位上默認(rèn)值null起沖突
- 如果兩個不同的元素根據(jù)不同的Hash值計(jì)算出了同一個角標(biāo)值,那么都能存進(jìn)去
- HashSet最后還是存入數(shù)組中, 只是根據(jù)元素的Hash值來確定存入的角標(biāo)位置
-
構(gòu)造方法
- HashSet() 構(gòu)造出一個新的集合, 底層數(shù)組默認(rèn)的初始容量是16(擴(kuò)容一倍),加載因子是0.75
- 加載因子: 集合中的數(shù)組可用的
- 角標(biāo)值得可選范圍越小,計(jì)算出重復(fù)角標(biāo)值得概率就越高
- HashSet(Collection<? extends E> c) 構(gòu)造一個包含指定collection中元素的新set
- HashSet() 構(gòu)造出一個新的集合, 底層數(shù)組默認(rèn)的初始容量是16(擴(kuò)容一倍),加載因子是0.75
-
常用方法
- boolean add( E e) 如果此set集合中尚未包含指定元素,則添加指定元素
- boolean remove(Object o) 移除某個元素 []
- int size() 獲取集合的長度
-
演示
- 需求: 去除ArrayList集合中的重復(fù)元素
public static void main(String[] args) { ArrayList<String> al = new ArrayList<>(); al.add("a"); al.add("s"); al.add("x"); al.add("a"); al.add("s"); HashSet<String> hs = new HashSet<>(al); System.out.println(hs); }
-
測試題
- 需求: 創(chuàng)建Student類, 定義name和age屬性, 創(chuàng)建多個對象(有屬性重復(fù)的對象)存入ArrayList集合中, 然后對集合中的元素去重
四. HashSet集合中元素保證唯一性
-
原理解析
- HashMap是通過調(diào)用元素的hashCode方法來比較兩個元素是否形同的,所有如果要保證引用數(shù)據(jù)類型邏輯上的唯一性,就必須重寫hashCode方法和equals方法
- 演示
public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }
- prime = 31 的原因
- 別人選的, 沒有原因
- 是質(zhì)數(shù), 和別的值計(jì)算得出的數(shù)重復(fù)的概率低
- 大小適中, 不會出現(xiàn)太大導(dǎo)致結(jié)果無法使用的問題
- 便于計(jì)算 2<<5 -1
-
引用數(shù)據(jù)類型除重
- 重寫hashCode和equals方法,自定義比較內(nèi)容
-
測試題
- 需求: 編寫程序, 獲取10個1到20的隨機(jī)數(shù), 要求隨機(jī)數(shù)不能重復(fù), 打印結(jié)果
public static void main(String[] args) { Random random = new Random(); HashSet<Integer> hashSet = new HashSet<>(); while(hashSet.size()<10){ int num = random.nextInt(20)+1; hashSet.add(num); } System.out.println(hashSet); }
-
測試題
- 需求: 從鍵盤錄入一行數(shù)據(jù), 去掉其中重復(fù)的字符,打印結(jié)果
public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("請輸入一行字符串:"); String line = sc.nextLine(); //將鍵盤錄入的字符串存儲在line中 char[] arr = line.toCharArray(); //將字符串轉(zhuǎn)換成字符數(shù)組 HashSet<Character> hs = new HashSet<>(); //創(chuàng)建HashSet集合對象 for (int i = 0; i < arr.length; i++) { hs.add(arr[i]); } Iterator<Character> it = hs.iterator(); while (it.hasNext()) { System.out.print(it.next()); } sc.close(); }
-
測試題
- 產(chǎn)生10個隨機(jī)的字符串, 要求不能重復(fù)(字符串中的字符取值在 'a' 到 'z' , 'A' 到 'Z' ,'0' 到 '9'),字符串的長度1-20
五. LinkedHashSet
-
定義
- 兼顧了linked的有序性和HashSet的元素唯一性
-
演示
public static void main(String[] args) { HashSet<String> hashSet = new HashSet<>(); hashSet.add("b"); hashSet.add("c"); hashSet.add("a"); hashSet.add("d"); System.out.println(hashSet); //結(jié)果 : [a, b, c, d] LinkedHashSet<String> lhs = new LinkedHashSet<>(); lhs.add("b"); lhs.add("c"); lhs.add("a"); lhs.add("d"); System.out.println(lhs); //結(jié)果: [b, c, a, d] }
六. TreeSet集合
-
定義
- TreeSet是一種順序的集合, 記住, 這里的順序是指集合中的元素有順序, 她是通過比較元素的大小來存放的, 大的存右邊,小的存左邊
- 存入的元素必須實(shí)現(xiàn)Comparable接口,并且重寫comparTo方法
- 最后存入的元素會形成一個樹狀結(jié)構(gòu)
-
構(gòu)造方法
- TreeSet() 構(gòu)造一個新的空set, 該set根據(jù)其元素的自然順序進(jìn)行排序
- TreeSet(Comparator <? super E> comparator) 構(gòu)建一個空的TreeSet, 他根據(jù)指定比較器進(jìn)行排序
-
常用方法
- add(E e) 將指定元素添加到此set
- first() 返回此set中當(dāng)前第一元素
- last() 返回此set中當(dāng)前最后一個元素
- floor() 返回此set中小于等于給定元素的最大元素,不存在則返回null
- higher() 返回此set中嚴(yán)格大于給定元素的最小元素,不存在則返回null
-
演示
public static void main(String[] args) { TreeSet<String> set = new TreeSet<>(); set.add("a"); set.add("d"); set.add("c"); set.add("b"); set.add("e"); set.add("b"); set.add("n"); System.out.println(set);//結(jié)果: [a, b, c, d, e, n] }
-
添加原理
- TreeSet會將第一個添加的元素作為中點(diǎn), 以后添加的元素會先跟中點(diǎn)進(jìn)行比較, 如果大于就接著跟所比較元素的右邊的元素接著比較,如果小于就接著跟所比較元素左邊的元素接著比較, 直到無法找到可比較的元素,就會將新添加的這個元素放到當(dāng)前位置
-
Comparable接口
- 所有存入的元素在比較的時(shí)候,如果集合沒有比較器, 就會將元素本身轉(zhuǎn)成Comparator類型并調(diào)用comparTo方法進(jìn)行比較, 所以我們必須實(shí)現(xiàn)Comparato接口,并重寫comparTo方法
- 調(diào)用compartTo方法比較
- 如果方法返回一個小于0的數(shù),表示當(dāng)前元素小于被比較元素
- 如果返回 0 表示當(dāng)前元素等于被比較元素
- 如果返回一個大于0的數(shù), 表示當(dāng)前元素大于被比較元素
- 演示
public class Student implements Comparable<Student>{ @Override public int compareTo(Student o) { return 0; } }
-
Comparator比較器
- 這是一個接口, 定義了一個compare抽象方法
- compare方法可以對兩個元素進(jìn)行比較
- 如果方法返回一個小于0的數(shù),表示參數(shù)1小于參數(shù)2
- 如果返回 0 ,表示參數(shù)1等于參數(shù)2
- 如果返回一個大于0的數(shù), 表示參數(shù)1大于參數(shù)2
- 演示
public class MyComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return 1; } }
-
注意事項(xiàng)
- TreeSet集合的存儲原理限定了必須要對存入的元素進(jìn)行比較
- 要么存入的元素自身實(shí)現(xiàn)Comparable接口, 要么提供外部的實(shí)現(xiàn)了Comparator接口的比較器, 否則程序運(yùn)行就會報(bào)錯
-
測試題
- 需求: 鍵盤錄入5個學(xué)生信息(姓名,語文成績,數(shù)學(xué)成績,英語成績),按照總分從高到低輸出到控制臺
- 演示
public class MyComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { int num = o2.getSum() - o1.getSum(); //根據(jù)學(xué)生的總成績降序排列 return num ; } }
public static void main(String[] args) { TreeSet<Student> set = new TreeSet<>(new MyComparator()); Student student1 = new Student("小紅",100); Student student2 = new Student("小明",50); Student student3 = new Student("小李",60); set.add(student1); set.add(student2); set.add(student3); for (Student student : set) { System.out.println(student); } }
七. 超級for
-
定義
- Iterator的簡寫形式
- 簡化數(shù)組和Collection集合的遍歷
-
格式
for(元素?cái)?shù)據(jù)類型 變量 : 數(shù)組或者Collection集合) { 使用變量即可,該變量就是當(dāng)前元素 }
-
演示
public static void main(String[] args) { TreeSet<Student> set = new TreeSet<>(new MyComparator()); Student student1 = new Student("小紅"); Student student2 = new Student("小明"); Student student3 = new Student("小李"); set.add(student1); set.add(student2); set.add(student3); for (Student student : set) { System.out.println(student); } }
-
三種迭代方式的刪除
- 普通for循環(huán),可以刪除,但是需要角標(biāo)
- 迭代器,可以刪除,但是必須使用迭代器自身的remove方法,否則會出現(xiàn)并發(fā)修改異常
- 超級for循環(huán)不能刪除
八. Collections工具類的使用
-
定義
- 一個用于操作Collection集合工具類
-
常用方法
- sort(List<T> list) 根據(jù)元素的自然順序排序
- swap(List<T> list , int i , int j) 交換集合中兩個角標(biāo)位上的值
- reverse(List<?> list ) 反轉(zhuǎn)集合中的元素的順序
- replaceAll(List<T> list, T oldVal, T newVal) 替換
-
演示
public static void main(String[] args) { List<String> cl = new ArrayList<>(); cl.add("a"); cl.add("d"); cl.add("s"); cl.add("t"); cl.add("a"); cl.add("e"); System.out.println(cl);//[a, d, s, t, a, e] Collections.sort(cl); System.out.println(cl);//[a, a, d, e, s, t] Collections.swap(cl,1,2); System.out.println(cl);//[a, d, a, e, s, t] Collections.reverse(cl); System.out.println(cl);//[t, s, e, a, d, a] Collections.replaceAll(cl,"a","f"); System.out.println(cl);//[t, s, e, f, d, f] }
總結(jié):
- ArrayList集合的嵌套
- 類似于二維數(shù)組
- Set
- 去重
- 無序(集合無序)
- HashSet
- 去重效率高, 尤其是在大數(shù)據(jù)量下
- 調(diào)用元素的hashCode和equals方法來比較是否相同
- LinkedHashSet
- 即有序(集合有序), 又能去重
- 效率不高
- TreeSet
- 去重, 元素有序(對存入的元素進(jìn)行排序)
- 要求存入的元素必須具備比較的能力或者提供第三方的比較器
- 元素具備比較的能力 : 元素實(shí)現(xiàn)comperable接口, 重寫comparTo方法
- 第三方比較器: 定義類,實(shí)現(xiàn)Compartor接口, 從寫 compare方法
- 超級for
- 迭代器的簡寫形式
- 優(yōu)點(diǎn): 格式簡單
- 缺點(diǎn): 無法進(jìn)行刪除操作
- Collections
- 一個用來操作List集合的工具類
作業(yè)
-
第一題
- 需求: 創(chuàng)建學(xué)生類, 定義姓名和年齡屬性, 創(chuàng)建多個學(xué)生對象, 根據(jù)學(xué)生的年齡進(jìn)行排序
package com.huwenlong.day14; //方法一 import java.util.Comparator; import java.util.TreeSet; //使用TreeSet和第三方比較器對學(xué)生的年齡進(jìn)行比較 public class Test01 { public static void main(String[] args) { TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getAge()-o2.getAge(); } }); treeSet.add(new Student("小紅",19)); treeSet.add(new Student("小明",20)); treeSet.add(new Student("小王",10)); treeSet.add(new Student("小趙",7)); for (Student student : treeSet) { System.out.println(student); } } } package com.huwenlong.day14; import java.util.Objects; public class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Objects.equals(name, student.name); } @Override public String toString() { final StringBuilder sb = new StringBuilder("Student{"); sb.append("name='").append(name).append('\''); sb.append(", age=").append(age); sb.append('}'); return sb.toString(); } @Override public int hashCode() { return Objects.hash(name, age); } public String getName() { return name; } public int getAge() { return age; } }
package com.huwenlong.day14; import java.util.LinkedList; //方法二:使用LinkedList并使用冒泡排序 public class TestDemo06 { public static void main(String[] args) { LinkedList<Student> linkedList = new LinkedList<>(); linkedList.add(new Student("小紅",19)); linkedList.add(new Student("小明",20)); linkedList.add(new Student("小王",10)); linkedList.add(new Student("小趙",7)); for (int i = 0; i < linkedList.size()-1; i++) { for (int j = 0; j < linkedList.size()-1-i; j++) { Student s1 = linkedList.get(j); Student s2 = linkedList.get(j+1); if(s1.getAge()>s2.getAge()){ Student t = s1; linkedList.set(j,s2); linkedList.set(j+1,t); } } } for (Student student : linkedList) { System.out.println(student); } } }
-
第二題
- 需求: 從鍵盤接收一個字符串, 程序?qū)ζ渲兴凶址M(jìn)行排序(重復(fù)不重復(fù)無所謂啦)
- 示例: 鍵盤輸入: helloitcast程序打印:acehillostt
package com.huwenlong.day14; //第一種方法:使用StringBuilder恰梢,可以處理重復(fù)字符 import java.util.Scanner; public class TestDemo07 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("請輸入一個字符串:"); String str = sc.next(); StringBuilder sb = new StringBuilder(str); for (int i = 0; i <sb.length()-1 ; i++) { for (int j = 0; j < sb.length()-1-i; j++) { if(sb.charAt(j)>sb.charAt(j+1)){ char ch = sb.charAt(j); sb.setCharAt(j,sb.charAt(j+1)); sb.setCharAt(j+1,ch); } } } System.out.println(sb.toString()); } }
package com.huwenlong.day14; //第二種方式:使用TreeNode砂代,不能處理重復(fù)字符 import java.util.Scanner; import java.util.TreeSet; public class Test02 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("請輸入一個字符串:"); String str = sc.next(); TreeSet<Character> treeSet = new TreeSet<>(); for (int i = 0; i < str.length(); i++) { treeSet.add(str.charAt(i)); } for (Character character : treeSet) { System.out.print(character); } } }
package com.huwenlong.day14; import java.util.ArrayList; import java.util.Collections; import java.util.Scanner; //使用Collections.sort方法進(jìn)行排序限府,可以處理重復(fù)字符 public class Test08 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String str = sc.next(); ArrayList<Character> list = new ArrayList<>(); for (int i = 0; i < str.length(); i++) { list.add(str.charAt(i)); } Collections.sort(list); for (Character character : list) { System.out.print(character); } } }
-
擴(kuò)展題
-
第一題
- 在一個集合中存儲了無序并且重復(fù)的字符串,定義一個方法,讓其有序(字典順序),而且還不能去除重復(fù)
- 使用TreeSet進(jìn)行排序
package com.huwenlong.day14; import java.util.ArrayList; import java.util.TreeSet; public class Test03 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("abcdef"); list.add("poiuy"); list.add("abcdef"); list.add("lkjghf"); list.add("xxzxsd"); list.add("poiuy"); ArrayList<String> newList = getSortedString(list); for (String s : newList) { System.out.println(s); } } //首先將ArrayList中的內(nèi)容傳入到TreeSet中瞎访,去重 public static ArrayList<String> getSortedString(ArrayList<String> list){ TreeSet<String> set = new TreeSet<>(list); ArrayList<String> newList = new ArrayList<>(); //然后對于去重后的元素望拖,計(jì)算出在原集合中出現(xiàn)的次數(shù)纱扭,再根據(jù)次數(shù)存到新字符串中 for (String s : set) { int count = 0; while(true){ int index = list.indexOf(s); if(index == -1) break; count++; list.remove(index); } for (int i = 0; i < count; i++) { newList.add(s); } } return newList; } }
-
-
第二題
- 思考, 能不能將HashSet集合可以存儲重復(fù)元素(同一個對象)
package com.huwenlong.day14; import java.util.HashSet; public class Test04 { public static void main(String[] args) { HashSet<Student> set = new HashSet<>(); Student stu = new Student("小紅",20); set.add(stu); System.out.println(set); set.add(stu); System.out.println(set); } } package com.huwenlong.day14; import java.util.Objects; public class Student { private String name; private int age; //自定義一個count屬性通過構(gòu)造方法初始化為0峡迷,然后重寫hashCode方法,每次調(diào)用hashCode都會將count++伍伤,根據(jù)name age count三個屬性計(jì)算哈希值,這樣同一對象的前后hash值不一樣就可以存儲同一個對象了 private int count; public Student(String name, int age) { this.name = name; this.age = age; count = 0; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Objects.equals(name, student.name); } @Override public String toString() { final StringBuilder sb = new StringBuilder("Student{"); sb.append("name='").append(name).append('\''); sb.append(", age=").append(age); sb.append('}'); return sb.toString(); } @Override public int hashCode() { count++; return Objects.hash(name, age ,count); } public String getName() { return name; } public int getAge() { return age; } }