1. 程式人生 > >Java 知識點整理-11.Java集合框架 Set+HashSet+LinkedHashSet+TreeSet+List和Set迭代方式的區別

Java 知識點整理-11.Java集合框架 Set+HashSet+LinkedHashSet+TreeSet+List和Set迭代方式的區別

目錄

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迴圈遍歷。