Java 知識點整理-11.Java集合框架 Set+HashSet+LinkedHashSet+TreeSet+List和Set迭代方式的區別
目錄
Set
1、Set集合概述及特點:
public interface Set<E> extends Collection<E> 不包含重複元素的集合。 更正式地,集合不包含一對元素e1和e2 ,使得e1.equals(e2) ,並且最多一個空元素。
Set介面繼承自Collection介面的所有建構函式(方法)。Set與Collection方法一模一樣。
2、Set集合子類如何保證元素唯一?
HashSet
1、Set集合子類HashSet<E>概述:
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable ,此類實現Set介面,由雜湊表(實際上是一個HashMap例項)支援。 它不保證set的迭代順序; 特別是它不保證該順序在一段時間內保持不變。此類允許使用null元素。翻譯:存取順序不一致,可以儲存null。
2、案例演示:HashSet儲存字串並遍歷。
import java.util.HashSet; /** * set集合中元素無索引,不可以重複,無序(存取順序不一致) */ public class Demo1_HashSet { public static void main(String[] args) { HashSet<String> hs = new HashSet<>(); //建立HashSet物件。 boolean b1 = hs.add("a"); boolean b2 = hs.add("a"); //當向Set集合中儲存重複元素的時候返回為false。 System.out.println(hs); //HashSet的繼承體系中有重寫toString方法 System.out.println(b1); //true System.out.println(b2); //false //HashSet的爺爺類AbstractCollection重寫了toString() /*public String toString() { Iterator<E> it = iterator(); //獲取迭代器 if (! it.hasNext()) //如果迭代器中沒有元素 return "[]"; //返回[] StringBuilder sb = new StringBuilder(); //如果有元素,建立StringBuilder sb.append('['); //將元素不斷進行新增 for (;;) { E e = it.next(); sb.append(e == this ? "(this Collection)" : e); if (! it.hasNext()) return sb.append(']').toString(); //最後將元素轉換成toString給返回 sb.append(',').append(' '); } }*/ System.out.println("----------------------------------"); hs.add("d"); hs.add("c"); hs.add("b"); System.out.println(hs); for (String string : hs) { //只要能使用迭代器迭代的,就可以使用增強for迴圈遍歷。 System.out.println(string); } } }
3、案例演示:HashSet儲存自定義物件,並保證元素唯一性。
import java.util.HashSet; import com.bean.Person; public class Demo2_HashSet { public static void main(String[] args) { HashSet<Person> hs = new HashSet<>(); hs.add(new Person("張三", 23)); hs.add(new Person("張三", 23)); hs.add(new Person("李四", 24)); hs.add(new Person("李四", 24)); hs.add(new Person("李四", 24)); hs.add(new Person("李四", 24)); //即使重寫了自定義類中的equals方法,依然所有元素都在。因為並沒有執行。 //重寫了hashCode方法後,保證元素唯一了,我們並沒有調equals方法,equals方法也跟著執行了。 //集合的每個物件元素進入集合的記憶體空間時,物件元素會自動調對應自定義類的hashCode()給自己算一個具體的hashCode值(地址值),每個物件的hashCode值都不一樣所以不會調equals()進行比較。因為地址值都不一樣,所以重複的物件都保留了。 //重寫hashCode()給定一個固定的值(任意),不讓自己算了。地址值相同的物件元素,呼叫equals()進行比較,如果元素屬性值相同,則返回值是true,就不存了。如果元素屬性不同,HashSet有桶結構,新元素會像桶一樣掛在被比較的元素上。再進來的元素會先與掛著的元素比較。 System.out.println(hs.size()); System.out.println(hs); } } 重寫hashCode()和equals()方法 public boolean equals(Object obj) { Person p = (Person)obj; return this.name.equals(p.name) && this.age == p.age; } public int hashCode() { return 10; }
程式碼優化:
為了減少比較,優化hashCode()程式碼寫法。最終版就是自動生成即可。
Alt + Shift + S + H 自動生成hashCode()和equals()。
//這麼複雜是為了少呼叫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;
}
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) { //呼叫物件的name屬性為null,name是自定義物件的屬性,是可以賦值為null的(不給賦值),不存在空指標異常。
if (other.name != null) //傳入物件的name屬性不為null
return false; //返回false
} else if (!name.equals(other.name))//呼叫物件的姓名不等於傳入物件的姓名
return false; //返回false
return true; //返回true
}
prime等於31是因為:
31是一個質數,質數是僅能被1和自己整除的數,這樣他的公約數就少了,就可以降低重複的可能性。
31這個數既不大也不小。大了,進行運算之後可能超過int的取值範圍,小了,最後相乘的結果可能還是重複的。能不重複儘量不重複。
31這個數好算,2的5次方減1,2向左移動5位再減去一。
4、HashSet如何保證元素唯一性的原理:
1.HashSet原理:
我們使用Set集合都需要去掉重複元素,如果在儲存的時候逐個元素呼叫equals()進行比較,效率較低。雜湊演算法提高了去重複的效率,降低了使用equals()的次數。
當HashSet呼叫add()方法儲存物件的時候,先呼叫物件的hashCode()方法得到一個雜湊值,然後在集合中查詢是否有雜湊值相同的物件。
如果沒有雜湊值相同的物件就直接存入集合。
如果有雜湊值相同的物件,就和雜湊值相同的物件逐個元素呼叫equals()進行屬性值比較,比較結果為false就存入,為true則不存入。
2.將自定義類的物件存入HashSet去重複:
類中必須同時重寫hashCode()和equals()。如果只重寫了hashCode(),儲存時hashCode值都不一樣,但是比較equals()全都為false,因為equals()比較是按照物件的地址值進行比較的,所以重複的都沒有去掉。
hashCode():屬性值相同的物件返回值必須相同,屬性值不同的物件返回值儘量不同(這樣做是提高效率,降低使用equals())。儘量不同說明有機率一樣,但儘量保證不一樣。
equals():屬性值相同返回true,屬性值不同返回false,返回false的時候儲存。
LinkedHashSet
1、LinkedHashSet概述:
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, Serializable,雜湊表和連結串列實現了Set介面,具有可預測的迭代次序。這種實現不同於HashSet,它維持於所有條 目的執行雙向連結串列。該連結串列定義了迭代排序,它是將元素插入集合(插入順序 ) 的順序 。請注意,如果一個元件被重新插入到組插入順序不受影響 。(元件e重新插入一組s如果當s.contains(e)將返回true之前立即呼叫s.add(e)被呼叫。)
普通方法全部繼承自父類以及其間接父類。
java.util包下,使用需要導包。
可以保證怎麼存就怎麼取,且能保證元素唯一。LinkedHashSet底層是連結串列實現的,是Set集合中唯一一個能保證怎麼存就怎麼取的集合物件。因為是HashSet的子類,所以也是保證元素唯一的,與HashSet的原理一樣。LinkedHashSet可以看成Linked和HashSet兩部分。
如果沒要求怎麼存就怎麼取,就用HashSet,HashSet效率稍微比LinkedHashSet高一點。
2、案例演示:產生10個1-20之間的隨機數要求隨機數不能重複。
需求:編寫一個程式,獲取10個1至20的隨機數,要求隨機數不能重複。並把最終的隨機數輸出到控制檯。
import java.util.HashSet;
import java.util.Random;
/**
* 需求:編寫一個程式,獲取10個1至20的隨機數,要求隨機數不能重複。並把最終的隨機數輸出到控制檯。
* 分析:
* 1.用Random類建立隨機數物件。
* 2.需要存取10個隨機數,而且不能重複。所以我們用HashSet集合。
* 3.條件:如果HashSet的size是小於10就可以不斷地儲存;如果大於等於10,就停止儲存。
* 4.通過Random類中的nextInt(n)獲取1到20之間的隨機數,並將這些隨機數儲存在HashSet集合中。
* 5.遍歷HashSet。
*/
public class Test1 {
public static void main(String[] args) {
//1.用Random類建立隨機數物件。
Random r = new Random();
//2.需要存取10個隨機數,而且不能重複。所以我們用HashSet集合。
HashSet<Integer> hs = new HashSet<>();
//3.條件:如果HashSet的size是小於10就可以不斷地儲存;如果大於等於10,就停止儲存。
while(hs.size() < 10) {
//4.通過Random類中的nextInt(n)獲取1到20之間的隨機數,並將這些隨機數儲存在HashSet集合中。
hs.add(r.nextInt(20) + 1);
}
//5.遍歷HashSet。
for (Integer integer : hs) {
System.out.println(integer);
}
}
}
3、練習1:使用Scanner從鍵盤讀取一行輸入,去掉其中重複字元,打印出不同的那些字元:aaaabbbcccddd。
import java.util.HashSet;
import java.util.Scanner;
/**
* 練習:使用Scanner從鍵盤讀取一行輸入,去掉其中重複字元,打印出不同的那些字元:aaaabbbcccddd。
* 分析:
* 1.建立Scanner物件。
* 2.建立HashSet物件,將字元儲存,去掉重複字元。
* 3.將字串轉換為字元陣列,獲取每一個字元儲存在HashSet集合中,自動去除重複。
* 4.遍歷HashSet集合,列印每一個字元。
*/
public class Test2 {
public static void main(String[] args) {
//1.建立Scanner物件。
Scanner sc = new Scanner(System.in);
System.out.println("請輸入一行字串:");
//2.建立HashSet物件,將字元儲存,去掉重複字元。
HashSet<Character> hs = new HashSet<>();
//3.將字串轉換為字元陣列,獲取每一個字元儲存在HashSet集合中,自動去除重複。
String line = sc.nextLine();
char[] arr = line.toCharArray();
//遍歷字元陣列。
for (char c : arr) { //char自動裝箱成包裝類Character。所以也可以直接寫Character。
hs.add(c);
}
//4.遍歷HashSet集合,列印每一個字元。
for(Character ch : hs) {//同理,這裡直接寫char也是可以的,自動拆箱。集合儲存物件,遍歷是當char遍歷。
System.out.print(ch);
}
}
}
4、練習2:將集合中的重複元素去掉。
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
/**
* 練習:將集合中的重複元素去掉。
* 分析:
* 1.建立一個List集合,儲存若干個重複元素。
* 2.單獨定義方法去除重複。
* 3.再列印一下List集合。
*/
public class Test3 {
public static void main(String[] args) {
//1.建立一個List集合,儲存若干個重複元素。
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("c");
list.add("c");
list.add("c");
list.add("c");
//2.單獨定義方法去除重複。
getSingle(list);
//3.再列印一下List集合。
System.out.println(list);
}
/**
* 分析:去除List集合中的重複元素。
* 1.建立一個LinkedHashSet集合。
* 2.將List集合中所有的元素新增到LinkedHashSet集合中。
* 3.將List集合中的元素清除。
* 4.將LinkedHashSet集合中的元素添加回List集合中。
*/
public static void getSingle(List<String> list) {
//1.建立一個LinkedHashSet集合。
LinkedHashSet<String> lhs = new LinkedHashSet<>();
//2.將List集合中所有的元素新增到LinkedHashSet集合中。
lhs.addAll(list);
//3.將List集合中的元素清除。
list.clear();
//4.將LinkedHashSet集合中的元素添加回List集合中。
list.addAll(lhs);
}
}
TreeSet
1、案例演示:TreeSet儲存Integer型別的元素並遍歷。
import java.util.TreeSet;
/**
* 案例演示:TreeSet儲存Integer型別的元素並遍歷。
* TreeSet集合是用來對元素進行排序的,同時也能保證元素的唯一。
*/
public class Demo3_TreeSet {
public static void main(String[] args) {
TreeSet<Integer> ts = new TreeSet<>();
ts.add(3);
ts.add(1);
ts.add(1);
ts.add(2);
ts.add(2);
ts.add(3);
ts.add(3);
System.out.println(ts);
}
}
2、TreeSet儲存自定義物件。
import java.util.TreeSet;
import com.bean.Person;
/**
* 自定義類的物件需要具備比較性,讓自定義類去實現Comparable介面,並重寫compareTo方法。依賴此功能即可保證TreeSet中元素唯一。保證元素唯一其實都是依賴一些功能的。就像HashSet必須重寫HashCode和equals方法。
* 當compareTo()返回0的時候,集合中只有一個元素。
* 當compareTo()返回正數的時候,集合會怎麼存就怎麼取(正序)。
* 當compareTo()返回負數的時候,集合會逆序儲存。
*/
public class Demo4_TreeSet {
public static void main(String[] args) {
TreeSet<Person> ts = new TreeSet<>();
ts.add(new Person("張三", 23)); //ClassCastException
ts.add(new Person("李四", 13));
ts.add(new Person("王五", 43));
ts.add(new Person("趙六", 33));
ts.add(new Person("張三", 23));
System.out.println(ts);
}
}
Comparable<T>介面概述:
public interface Comparable<T>,該介面對實現它的每個類的物件強加一個整體排序。 這個排序被稱為類的自然排序 ,類的compareTo方法被稱為其自然比較方法 。引數型別:T - 可以將此物件與之進行比較的物件型別。
Collections.sort (和Arrays.sort )可以自動對實現此介面的物件進行列表(和陣列)排序。
Comparable<T>介面的普通方法:
int compareTo(T o),將此物件與指定的物件進行比較以進行排序。 返回一個負整數,零或正整數,因為該物件小於,等於或大於指定物件。
3、TreeSet自然儲存自定義物件和保證元素唯一的原理:
根據compareTo返回值。TreeSet底層是二叉樹。
二叉樹:兩個叉。小的儲存在左邊(返回正數),大的儲存在右邊(返回負數),相等就不存(返回0)。
因此compareTo(),在TreeSet集合中如何儲存元素取決於compareTo()的返回值。
ⅰ.返回值為0,集合中只有一個元素。首先第一個元素進集合作為二叉樹的根,第二個元素進來呼叫compareTo(),compareTo()返回值是0,不儲存該元素,同理剩餘元素也不儲存。
ⅱ.返回值為-1(負數),集合會將儲存的元素倒序。首先第一個元素進集合作為二叉樹的根,第二個元素進來呼叫compareTo(),compareTo()返回值是-1,-1表示該元素比根元素要小,所以儲存在根元素的左邊,第三個元素進來呼叫compareTo()與根元素作比較,compareTo()返回值是-1;再與第二個元素作比較,compareTo()返回值還是-1,成為第二個元素的左子樹。同理其餘元素進集合依次從根向下比較,返回值是-1就成為最底層元素的左子樹。取的時候,樹底層左邊元素是最小的,再逐層依次向上取。
ⅲ.返回值為1(正數),集合會怎麼存就怎麼取。首先第一個元素進集合作為二叉樹的根,第二個元素進來呼叫compareTo(),compareTo()返回值是1,1表示該元素比根元素要大,所以儲存在根元素的右邊,第三個元素進來呼叫compareTo()與根元素作比較,compareTo()返回值是1;再與第二個元素作比較,compareTo()返回值還是1,成為第二個元素的右子樹。同理其餘元素進集合依次從根向下比較,返回值是1就成為最底層元素的右子樹。取的時候,根元素是最小的,再逐層依次向下取。
總結:比根元素小的存左邊,比根元素大的存右邊。比根元素大,比右子樹小的存右子樹左邊。
取的時候從左邊最小的開始取,取完根元素,到根元素的右邊,先看右子樹有沒有他的左子樹,有就先取他的左子樹,再取他本身。
舉例:自定義物件實現Comparable介面後重寫的compareTo():
//按照年齡排序 public int compareTo(Person o) { int num = this.age - o.age; //年齡是比較的主要條件 return num == 0 ? this.name.compareTo(o.name) : num;//姓名是比較的次要條件 }
String中重寫了compareTo方法:int compareTo(String anotherString) 按字典順序比較兩個字串。
4、練習1:案例演示:TreeSet儲存自定義物件並遍歷練習1(按照姓名排序)
import java.util.TreeSet;
import com.bean.Person;
/**
* 案例演示:TreeSet儲存自定義物件並遍歷練習1(按照姓名排序)
*/
public class Test4 {
public static void main(String[] args) {
TreeSet<Person> ts = new TreeSet<>();
ts.add(new Person("李四", 13));
ts.add(new Person("張三", 23));
ts.add(new Person("王五", 43));
ts.add(new Person("趙六", 33));
System.out.println('張' + 0); //24352
System.out.println('李' + 0); //26446
System.out.println('王' + 0); //29579
System.out.println('趙' + 0); //36213
System.out.println(ts);
}
}
自定義類中重寫的compareTo()
//按照姓名排序
public int compareTo(Person o) {
int num = this.name.compareTo(o.name); //姓名是比較的主要條件
return num == 0 ? this.age - o.age : num; //年齡是比較的次要條件
}
5、練習2:案例演示:TreeSet儲存自定義物件並遍歷練習2(按照姓名的長度排序)
import java.util.TreeSet;
import com.bean.Person;
/**
* 案例演示:TreeSet儲存自定義物件並遍歷練習2(按照姓名的長度排序)
*/
public class Test5 {
public static void main(String[] args) {
TreeSet<Person> ts = new TreeSet<>();
ts.add(new Person("zhangsan", 23));
ts.add(new Person("lisi", 13));
ts.add(new Person("wangwu", 33));
ts.add(new Person("zhaoliu", 43));
ts.add(new Person("aaaa", 53));
System.out.println(ts);
}
}
自定義類中重寫的compareTo()
//按照姓名的長度排序
public int compareTo(Person o) {
int length = this.name.length() - o.name.length(); //長度是比較的主要條件
int num = length == 0 ? this.name.compareTo(o.name) : length;//內容是比較的次要條件
return num == 0 ? this.age - o.age : num; //年齡是比較的次要條件
}
6、比較器排序:
案例演示:TreeSet保證元素唯一和比較器排序的原理及程式碼實現。
import java.util.Comparator;
import java.util.TreeSet;
/**
* 案例演示:TreeSet保證元素唯一和比較器排序的原理及程式碼實現。
*/
public class Test6 {
public static void main(String[] args) {
//需求:將字串按照長度排序
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);
}
}
class CompareByLen implements Comparator<String> { //CompareByLen使我們自己定義的比較器。
//重寫了compare(),之所以沒重寫equals()也沒報錯,是因為所有類預設繼承Object。所以從物件類裡繼承了equals方法。實現介面時,只要有該方法即可。
public int compare(String s1, String s2) { //按照字串的長度比較
int num = s1.length() - s2.length(); //長度為比較的主要條件
return num == 0 ? s1.compareTo(s2) : num; //內容為比較的主要條件
}
}
首先,第一個元素進樹集作根元素。第二個元素進樹集,為s1,根元素為s2,用s1的長度減去s2的長度,跟compareTo()原理相同,正右負左零不存。第三個元素進樹集,為s1,根元素為s2,用s1的長度減去s2的長度,正右負左零不存,如果第三個元素與第二個元素符號相同(正、負或0),則出現在根元素的同一邊,即需要再做一次比較。此時,第三個元素為s1,第二個元素為s2,用s1的長度再減去s2的長度,正右負左零不存。其餘元素與之前原理相同。
總結:誰來呼叫這個實現了Comparator介面的方法誰就是s1,集合中的元素就是s2。也是Compare()的返回值決定了排序方法,正右負左零不存。
7、TreeSet的構造方法:
TreeSet(Comparator<? super E> comparator) 構造一個新的,空的TreeSet(樹集),根據指定的比較器進行排序。
Comparator<T>介面概述:
public interface Comparator<T>,比較功能,對一些物件的集合施加了一個整體排序 。 可以將比較器傳遞給排序方法(如Collections.sort或Arrays.sort ),以便對排序順序進行精確控制。java.util包下,需要導包。
Comparator<T>介面方法:
int compare(T o1, T o2) 比較其兩個引數的順序。 返回負整數,零或正整數,因為第一個引數小於,等於或大於第二個引數。跟compareTo的原理相同,正右負左零不存。
boolean equals(Object obj) 指示某個其他物件是否等於此比較器。 該方法必須遵守Object.equals(Object)的一般合同。 另外, 只有指定的物件也是一個比較器,這個方法只能返回true ,並且它與這個比較器的順序相同。
8、TreeSet原理:
1.特點:
TreeSet是用來排序的,可以指定一個順序,物件存入之後會按照指定的順序排列。TreeSet有兩種排序方式:自然排序和比較器排序。
2.使用方式:
ⅰ.自然順序(Comparable) 需要你儲存的這個類去實現Comparable介面,這個類建立的物件要具備一個比較的功能(重寫Comparable介面內的compareTo())。
原理:
TreeSet類的add()方法中會把存入的物件提升為Comparable型別。不實現Comparable介面,就會報型別轉換異常異常。
呼叫物件的compareTo()和集合中的物件比較。哪個元素存進去,誰就調compareTo()。把集合中的元素當做引數傳進來與呼叫該方法的元素進行比較。
根據compareTo()返回的結果進行儲存(正右負左零不存)。
ⅱ.比較器順序(Comparator)。
原理:
建立TreeSet的時候可以制定 一個Comparator。
如果傳入了Comparator的子類物件,那麼TreeSet就會按照比較器中的順序排序。
add()方法內部會自動呼叫Comparator介面中compare()排序。
呼叫物件的是compare方法的第一個引數,集合中的物件是compare方法的第二個引數。
ⅲ.兩種方式的區別:
TreeSet建構函式什麼都不傳,預設按照類中Comparable的順序去比較(沒有Comparable就報ClassCastException)。
TreeSet如果傳入Comparator比較器,就會優先按照Comparator去排序。
9、比較器練習1:在一個集合中儲存了無序並且重複的字串,定義一個方法,讓其有序(字典順序),而且還不能去除重複。
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
/**
* 練習:在一個集合中儲存了無序並且重複的字串,定義一個方法,讓其有序(字典順序),而且還不能去除重複。
* 分析:
* 1.定義一個List集合,並存儲重複的無序的字串。
* 2.定義方法對其排序,保留重複。
* 3.列印List集合。
*/
public class Test7 {
public static void main(String[] args) {
//1.定義一個List集合,並存儲重複的無序的字串。
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("aaa");
list.add("ccc");
list.add("ddd");
list.add("fffffffffff");
list.add("niubi");
list.add("itcast");
list.add("bbbb");
list.add("aaa");
list.add("aaa");
//2.定義方法對其排序,保留重複。
sort(list);
//3.列印List集合。
System.out.println(list);
}
/**
* 定義方法,排序並保留重複。
* 分析:
* 1.建立TreeSet集合物件。因為String本身就具備比較功能,但是重複不會保留,所以我們用比較器。
* 2.將list集合中所有的元素新增到TreeSet集合中,對其排序,保留重複。
* 3.清空list集合。
* 4.將TreeSet集合中排好序的元素新增到List集合中。
*/
public static void sort(List<String> list) {
//1.建立TreeSet集合物件。因為String本身就具備比較功能,但是重複不會保留,所以我們用比較器。
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() { //new Comparator<>(){}代表Comparator的子類物件(實現了Comparator介面的方法)。匿名內部類。
public int compare(String s1, String s2) {
int num = s1.compareTo(s2); //比較內容為主要條件
return num == 0 ? 1 : num; //保留重複
}
});
//2.將list集合中所有的元素新增到TreeSet集合中,對其排序,保留重複。
ts.addAll(list);
//3.清空list集合。
list.clear();
//4.將TreeSet集合中排好序的元素新增到List集合中。
list.addAll(ts);
}
}
10、比較器練習2:從鍵盤接收一個字串,程式對其中所有字元進行排序,例如鍵盤輸入:helloitcast。程式列印:acehillostt。
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
/**
* 練習:從鍵盤接收一個字串,程式對其中所有字元進行排序,例如鍵盤輸入:helloitcast。程式列印:acehillostt。
* 分析:
* 1.鍵盤錄入字串,Scanner。
* 2.將字串轉換成字元陣列。
* 3.定義TreeSet集合,傳入比較器對字元排序並保留重複。
* 4.遍歷字元陣列,將每一個字元儲存在TreeSet集合中。
* 5.遍歷TreeSet集合,列印每一個字元。
*/
public class Test8 {
public static void main(String[] args) {
//1.鍵盤錄入字串,Scanner。
Scanner sc = new Scanner(System.in);
System.out.println("請輸入一個字串:");
String line = sc.nextLine();
//2.將字串轉換成字元陣列。
char[] arr = line.toCharArray();
//3.定義TreeSet集合,傳入比較器對字元排序並保留重複。
TreeSet<Character> ts = new TreeSet<>(new Comparator<Character>() {
public int compare(Character c1, Character c2) {
// int num = c1.compareTo(c2); //作用相同
int num = c1 - c2; //自動拆箱,轉換成int數運算
return num == 0 ? 1 : num;
}
});
//4.遍歷字元陣列,將每一個字元儲存在TreeSet集合中。
for(char c : arr) {
ts.add(c); //自動裝箱
}
//5.遍歷TreeSet集合,列印每一個字元。
for(Character c : ts) {
System.out.print(c);
}
}
}
Character重寫了compareTo():
public int compareTo(character anotherCharacter) 根據數字比較兩個 Character物件。所以直接TreeSet儲存char字元元素,重複也會直接被幹掉,所以只能寫一個比較器。八種資料型別包裝類都實現了Comparable並重寫了comparet()。
11、練習:程式啟動後,可以從鍵盤輸入接收多個整數,直到輸入quit時結束輸入。把所有輸入的整數倒序排列列印。
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
/**
* 練習:程式啟動後,可以從鍵盤輸入接收多個整數,直到輸入quit時結束輸入。把所有輸入的整數倒序排列列印。
* 分析:
* 1.建立Scanner物件,鍵盤錄入。
* 2.建立TreeSet集合物件,TreeSet集合中傳入比較器。
* 3.無限迴圈不斷接收整數,遇到quit退出。因為退出時quit,所以鍵盤錄入的時候應該都以字串的形式錄入。
* 4.判斷是quit就退出,不是就將其轉換為Integer,並新增到集合中。
* 5.遍歷TreeSet集合並列印每一個元素。
*/
public class Test9 {
public static void main(String[] args) {
//1.建立Scanner物件,鍵盤錄入。
Scanner sc = new Scanner(System.in);
System.out.println("請輸入多個整數:");
//2.建立TreeSet集合物件,TreeSet集合中傳入比較器。
TreeSet<Integer> ts = new TreeSet<>(new Comparator<Integer>() {
public int compare(Integer i1, Integer i2) {
// int num = i2 - i1; //自動拆箱,倒序排列。
int num = i2.compareTo(i1);
return num == 0 ? 1 : num;
}
});
//3.無限迴圈不斷接收整數,遇到quit退出。因為退出時quit,所以鍵盤錄入的時候應該都以字串的形式錄入。
while(true) {
String line = sc.nextLine(); //將鍵盤錄入的字串儲存在line中 。
//4.判斷是quit就退出,不是就將其轉換為Integer,並新增到集合中。
if("quit".equals(line)) {
break;
}
try {
Integer i = Integer.parseInt(line); //將數字字串轉換成數字c,自動裝箱。
ts.add(i);
} catch (Exception e) {
System.out.println("您錄入的資料有誤,請輸入一個整數:");
}
}
//5.遍歷TreeSet集合並列印每一個元素。
for (Integer integer : ts) {
System.out.println(integer);
}
}
}
12、案例演示:鍵盤錄入學生資訊按照總分排序後輸出在控制檯。
需求:鍵盤錄入5個學生資訊(姓名,語文成績,數學成績,英語成績),按照總分從高到低輸出到控制檯。
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
import com.bean.Student;
/**
* 需求:鍵盤錄入5個學生資訊(姓名,語文成績,數學成績,英語成績),按照總分從高到低輸出到控制檯。
* 分析:
* 1.定義一個學生類。
* 成員變數:姓名,語文成績,數學成績,英語成績,總成績。
* 成員方法:空參、有參構造,有參構造的引數分別是姓名,語文成績,數學成績,英語成績。toString(),在遍歷集合中的Student物件、列印物件引用的時候顯示屬性值。
* 2.鍵盤錄入Scanner,建立鍵盤錄入物件。
* 3.建立TreeSet集合物件,在TreeSet的建構函式中傳入比較器,按照總分比較。
* 4.錄入五個學生,並以集合中的學生個數為判斷條件,如果size小於五就進行儲存。
* 5.將錄入的字串用逗號切割,會返回一個字串陣列,將字串陣列中的元素從第二個元素開始轉換成int數。
* 6.將轉換後的節後封裝成Student物件,將Student物件新增到TreeSet集合中。
* 7.遍歷TreeSet集合,列印每一個Student物件。
*/
public class Test10 {
public static void main(String[] args) {
//2.鍵盤錄入Scanner,建立鍵盤錄入物件。
Scanner sc = new Scanner(System.in);
System.out.println("請輸入學生成績:(格式:姓名,語文成績,數學成績,英語成績)");
//3.建立TreeSet集合物件,在TreeSet的建構函式中傳入比較器,按照總分比較。
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
public int compare(Student s1, Student s2) {
int num = s2.getSum() - s1.getSum();
return num == 0 ? 1 : num;
}
});
//4.錄入五個學生,並以集合中的學生個數為判斷條件,如果size小於五就進行儲存。
while(ts.size() < 5) {
//5.將錄入的字串用逗號切割,會返回一個字串陣列,將字串陣列中的元素從第二個元素開始轉換成int數。
String line = sc.nextLine();
try {
String[] arr = line.split(",");
int chinese = Integer.parseInt(arr[1]); //轉換語文成績
int math = Integer.parseInt(arr[2]); //轉換數學成績
int english = Integer.parseInt(arr[3]); //轉換英語成績
//6.將轉換後的節後封裝成Student物件,將Student物件新增到TreeSet集合中。
ts.add(new Student(arr[0], chinese, math, english));
} catch (Exception e) {
System.out.println("錄入格式有誤,輸入5個學生成績格式是:(姓名,語文成績,數學成績,英語成績");
}
}
//7.遍歷TreeSet集合,列印每一個Student物件。
System.out.println("排序後的學生資訊:");
for (Student s : ts) {
System.out.println(s);
}
}
}
自定義類:
public class Student {
private String name;
private int chinese;
private int math;
private int english;
private int sum;
public Student() {
super();
}
public Student(String name, int chinese, int math, int english) {
super();
this.name = name;
this.chinese = chinese;
this.math = math;
this.english = english;
this.sum = this.chinese + this.math + this.english;
}
public int getSum() {
return sum;
}
public String toString() {
return name + "," + chinese + "," + math + "," + english + "," + sum;
}
}
List和Set迭代方式的區別:
1、List(4種迭代方式)
普通for迴圈, 使用get()逐個獲取。
呼叫iterator()方法得到Iterator, 使用hasNext()和next()方法。
增強for迴圈, 只要可以使用Iterator的類都可以用。
Vector集合可以使用Enumeration的hasMoreElements()和nextElement()方法。
2、Set
a.呼叫iterator()方法得到Iterator, 使用hasNext()和next()方法。
b.增強for迴圈, 只要可以使用Iterator的類都可以用。
3、總結:
只要能用迭代器迭代的,都能用增強for迴圈遍歷。