1. 程式人生 > >Java基礎16--集合框架

Java基礎16--集合框架

16-1,集合框架Vector集合

1,Vector是向量,底層為陣列結構,可以通過其elements()方法獲取一個Enumeration物件,這個物件的功能於Iterator的功能相同,Iterator比他多了移除的操作,所以現在一般不用Vector,都用Iterator。

通過Enumeration迭代Vector集合的元素示例:

public class VectorDemo {
	public static void main(String[] args) {
		Vector v = new Vector();
		v.addElement("abc1");
		v.addElement("abc2");
		v.addElement("abc3");
		v.addElement("abc4");
		Enumeration en = v.elements();
		while(en.hasMoreElements()) {
			System.out.println("nextElements:" + en.nextElement());
		}
		Iterator it = v.iterator();
		while(it.hasNext()) {
			System.out.println("next:" + it.next());
		}
	}
}

用Iterator替代Enumeration是因為Enumeration和裡面的方法名字太長,簡化書寫。

 

16-2,LinkedList集合

1,基本操作

addFirst():向頭部新增一個元素。

getFirst():獲取第一個元素但不刪除。

removeFirst():獲取第一個元素並刪除。

2,示例:

public class Demo {
	public static void main(String[] args) {
		LinkedList link = new LinkedList();
		link.addFirst("abc1");
		link.addFirst("abc2");
		link.addFirst("abc3");
		link.addFirst("abc4");
		System.out.println(link);
		System.out.println(link.getFirst());//abc4
		System.out.println(link.getFirst());//abc4,不刪除
		//System.out.println(link.removeFirst());//abc4
		//System.out.println(link.removeFirst());//abc3
		while(!link.isEmpty()) {
			System.out.println(link.removeLast());//abc1,abc2,abc3,abc4
		}
	}
}

 

16-3,LinkedList練習-模擬棧和佇列

需求:請使用LinkedList來模擬一個堆疊或者佇列資料結構。

堆疊:先進後出。

佇列:先進先出。

我們應該描述一個這樣的容器,給使用者提供一個容器物件完成這兩種結構中的一種。

public class LinkedTest {
	public static void main(String[] args) {
		DuiLie d1 = new DuiLie();
		d1.myAdd("abc1");
		d1.myAdd("abc2");
		d1.myAdd("abc3");
		d1.myAdd("abc4");
		while(!d1.isNull()) {
			System.out.println(d1.myGet());
		}
	}
}
class Duilie {
	private LinkedList list;
	public DuiLie() {
		list = new LinkedList();
	}
	//佇列的新增元素功能
	public void myAdd(Object obj) {
		list.addLast(obj);
	}
	public Object myGet() {
		return list.removeFirst();
	}
	public boolean isNull() {
		return list.isEmpty();
	}
}

實現了佇列。

 

16-4,ArrayList儲存自定義物件

例如儲存Person物件。

Person類:

public class Person {
	private String name;
	private int age;
	public Person() {
		super();
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return this.name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return this.age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

ArrayList儲存自定義物件Person類。

public class ArrayListTest {
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		al.add(new Person("lisi1",21));
		al.add(new Person("lisi2",22));
		al.add(new Person("lisi3",23));
		al.add(new Person("lisi4",24));
		Iterator it = al.iterator();
		while(it.hasNext()) {
			//System.out.println(((Person)it.next()).getName()+"::"+((Person)it.next()).getAge());
			/*
			上面這種方法會出現錯誤結果,在SOP中,每next一次,就想下走一個物件,也就是說,
			第一個next讀的是lisi1的name,然後跳轉至下一個物件,後面的next讀的是lisi2的age,
			所以出現lisi1,22,lisi3,24的結果;
			下面的方法,先建立一個物件把讀取的物件賦給p,再SOP就不會出錯了。
			*/
			Person p = (Person)it.next();
			System.out.println(p.getName()+"---"+p.getAge());
		}
	}
}

 

16-5,HashSet集合

1,Set集合:元素不可以重複,取出元素的順序和存入的順序不一定一致,是無序的。無序是因為HashSet中的元素的地址使用系統的雜湊演算法算出來的,雜湊演算法算出的位置是無序的,故無序。

Set介面中的方法和Collection一致。

    HashSet內部資料結構是雜湊表,是不同步的,集合中的元素是唯一的。

示例:

public class HashSetDemo {
	public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add("hehe");
		hs.add("heihei");
		hs.add("haha");
		hs.add("xixi");
		//hs.add("hehe");//如果有兩個hehe,則最後只打印一個,因為不許重複
		Iterator it = hs.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
	}
}

此程式的輸出結果是無序的。

 

16-6,雜湊表

1,雜湊表是雜湊演算法,是核心的一種儲存資料的演算法,查詢起來非常快,其底層為陣列結構,雜湊演算法可以根據資料的屬性來算出相應的在陣列中的位置,直接將資料儲存進去,取得時候不用遍歷,將這個資料呼叫hashCode方法,算出位置,去相應的位置有沒有該元素,就不用找其他位置的元素是否匹配了。

2,雜湊表確定元素是否相同的步驟:

(1)判斷的是兩個元素的雜湊值是否相同,如果相同,再判斷兩個物件的內容是否相同。

(2)判斷雜湊值相同,其實判斷的是物件的hashCode方法,判斷內容相同,用的是equals方法。

注意:如果雜湊值不同,是不需要判斷equals的。

 

16-7,HashSet儲存自定義物件

1,如何保證HashSet的元素的唯一性呢?

是通過物件的hashCode方法和equals方法來完成確定物件的唯一性的。

如果物件的hashCode值不同,那麼不用判斷equals方法,就直接儲存到雜湊表中。

如果物件的hashCode值相同,那麼要再次判斷物件的equals方法是否為true,如果為true,視為相同元素,就進行儲存。

記住:如果元素要儲存到HashSet集合中,必須覆蓋hashCode方法和equals方法。

一般情況下,如果定義的類會產生很多物件,比如:人,學生,書,通常都需要覆蓋equals,hashCode方法。

這是判斷物件是否相同的依據。

2,儲存自定義物件例項:

public class Person {
	private String name;
	private int age;
	public Person() {
		super();
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return this.name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return this.age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	//重寫hashCode和equals方法
	@Override
	public int hashCode() {
		return name.hashCode() + age * 27;
	}
	@Override
	public boolean equals(Object obj) {
		if(this == obj) {
			return true;
		}
		if(!(obj instanceof Person)) {
			throw new ClassCastException("型別錯誤");
		}
		Person p = (Person)obj;
		return this.name.equals(p.name) && this.age == p.age;
	}
}

儲存自定義物件類:

//往HashSet集合中儲存Person物件,如果姓名和年齡相同,視為同一個人,相同元素。
public class HashSetTest {
	public static void main(String[] args) {
		HashSet hs = new HashSet();
		/*
		HashSet結合資料結構是雜湊表,所以儲存元素的時候,
		使用元素的hashCode方法來確定位置,如果位置相同,
		再通過元素的equals方法來確定是否相同。
		*/
		hs.add(new Person("lisi4",24));
		hs.add(new Person("lisi7",27));
		hs.add(new Person("lisi1",21));
		hs.add(new Person("lisi9",29));
		hs.add(new Person("lisi7",27));//若不重寫hashCode和equals方法,這裡也會輸出lisi7,27,不保證唯一性
		Iterator it = hs.iterator();
		while(it.hasNext()) {
			Person p = (Person)it.next();
			System.out.println(p.getName()+"..."+p.getAge());	
		}
	}
}

 

16-8,集合框架-練習

需求:取出ArrayList中的重複元素。

public class ArrayListTest{
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		al.add("abc1");
		al.add("abc2");
		al.add("abc2");
		al.add("abc1");
		al.add("abc");
		System.out.println(al);
		al = getSingleElement(al);
		System.out.println(al);
	}
	public static ArrayList getSingleElement(ArrayList al) {
		//定義一個臨時容器
		ArrayList temp = new ArrayList();
		//迭代al集合
		Iterator it = al.iterator();
		while(it.hasNext()) {
			Object obj = it.next();
			//判斷被迭代到的元素是否在臨時容器中存在。
			if(!temp.contains(obj)) {
				temp.add(obj);
			}
		}
		return temp;
	}
}

結果:abc1,abc2,abc2,abc1,abc

    abc1,abc2,abc

 

16-9,LinkedHashSet集合

特點:具有可迭代順序的Set介面的雜湊表,和連結列表實現。

說白了,此集合可以保證物件唯一且有序。而HashSet是無序的。

例如:

public class LinkedHashSetDemo {
	public static void main(String[] args) {
		HashSet hs = new LinkedHashSet();
		hs.add("haha");
		hs.add("hehe");
		hs.add("heihei");
		hs.add("xixi");
		Iterator it = hs.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
	}
}

結果:haha hehe heihei xixi

輸入和輸出順序是相同的。

 

16-10,TreeSet集合

1,這個集合可以對Set集合中的元素進行指定順序的排序(按自然順序進行排序)。

例如:

TreeSet ts = new TreeSet();
ts.add("zhangsan",22);
ts.add("lisi",23);
ts.add("wangwu",27);
迭代...

發現第二個ts.add報錯。

原來,TreeSet要進行排序,就必須要具備排序這個功能,就Person來說,排序功能如何實現呢?

應該用java.lang中的Comparable介面,Person要想具備排序功能,實現Comparable介面即可。

Comparable介面的描述:

此介面強行對實現它的每一個類的物件進行整體排序,這種排序被稱為類的自然排序,類的compareTo方法被稱為他的自然比較方法。

 

2,TreeSet判斷物件唯一性的方式,就是根據比較方法的返回結果是否為0,是0,就是相同元素,不存進去。在示例中,如果Person實現了Comparable介面並重寫了compareTo方法,並且其返回0,則再執行時不會報錯,但是隻會打印出存的第一個元素,即:zhangsan,22,因為要存第二個的時候,會呼叫compareTo方法判斷是否與上一個元素相同,由於返回結果是0,即表示相同,所以後面的元素就都不往裡存了,所以只打印第一個元素。

3,以Person物件的年齡進行從小到大的排序。

將上面的Person類實現Comparable介面,然後在Person中覆寫compareTo方法。

//若要按照從大到小排序,把1變為-1,把-1變為1即可,或把this變為p,把p變為this.
public int compareTo(Object obj) {
	Person p = (Person)obj;
	if(this.age > p.age)
		return 1;
	if(this.age < p.age)
		return -1;
	return 0;
}

但是如果年齡相同,則後一個就打印不出來了,即:列印時少了一個Person。

這時應該加條件,如果主要條件相同,則應該比較次要條件。

修改後的compareTo方法:

public int compareTo(Object obj) {
	Person p = (Person)obj;
	if(this.age > p.age) 
		return 1;
	if(this.age < p.age) 
		return -1;
	else
		/*
		String類中的compareTo方法也來自於Comparable介面,
		String類實現了Comparable介面
		*/
		return this.name.compareTo(p.name);
}

但是這麼寫爛透了...,優化:

public int compareTo(Object obj) {
	Person p = (Person)obj;
	int temp = this.age - p.age;
	return temp == 0 ? this.name.compareTo(p.name) : temp;
}

之前在TreeSet中存String類的物件,可以進行排序,是因為String類實現了Comparable介面。

 

總結:TreeSet對元素進行排序的方式之一:

讓元素自身具備比較功能,元素需要實現Comparable介面,覆蓋compareTo方法。

 

16-11,TreeSet集合,Comparator比較器

1,如果不要按照物件中具備的自然順序,如果物件中不具備自然順序,怎麼辦?

可以使用TreeSet的第二種排序方式:讓集合自身具備比較功能。

2,TreeSet的構造器中有一個帶有Comparator引數的構造器,在建立TreeSet物件時傳入此Comparator的物件,該TreeSet可按指定的Comparator比較器進行排序。

3,java.util

介面:Comparator<T>

強行對某個物件Collection進行整體排序的比較函式。

4,單獨建立一個Java檔案,建立一個根據Person類的name進行排序的比較器。

public class ComparatorsByName implements Comparator {
	public int compare(Object o1,Object o2) {
		Person p1 = (Person)o1;
		Person p2 = (Person)o2;
		int temp = p1.getName().compareTo(p2.getName());
		return temp == 0 ? p1.getAge() - p2.getAge() : temp;
	}
}

然後,在建立TreeSet集合的時候,直接:

TreeSet ts = new TreeSet(new ComparatorsByName());

即可。

5,如果Person物件本身實現了Comparable介面,覆蓋了compareTo方法,而且把Person物件存入了已經實現了比較器的TreeSet中,那麼以誰的比較功能為主呢?

以比較器的功能為主,且實際開發中比較器比較常用。

實際上Java中的很多類都實現了Comparable介面,使其物件具備比較功能。

6,使集合自身具備比較功能的步驟:

定義一個類實現Comparator介面,覆蓋compare方法,將該類物件作為引數傳遞個TreeSet集合的建構函式。

 

16-12,TreeSet-二叉樹

1,TreeSet排序功能在底層是如何實現的呢?

他的排序功能在底層是通過二叉樹來實現的。

以下列TreeSet的新增為例,用圖表示(根據Person的age排序),

第一個進去的元素不用進行比較,以後進去的元素都要與前面的元素比較。


步驟:

第一個28進來以後,發現前面沒有元素,不做比較,直接放在第一個位置。

21進來後,跟28比較,比28小,放在28的左邊。

29進來後先跟28比較,比28大,放在28的右邊,就不用和21比較了。

25進來後先跟28比較,比28小,到左邊,再跟21比較,比21大,則放在21的右邊。

......

在樹中,每個節點都有自己的屬性,以值為21的節點舉例:


21有三個引用,分別記錄與之連結的父節點,以及左子節點和右子節點,以記錄整個集合各個元素的位置,但28沒有父,19沒有左和右。

但是樹的規模一大,就涉及到效率問題,如何解決呢?

其實,只要樹一排完,該集合就已經是一個有序的集合了,有序集合的查詢,就可以想到用二分查詢法。

其實每次向裡面新增元素的時候,都會對已有的元素進行折半,二分查詢,以確定新元素的位置,效率較高。

 

2,如何做到怎麼存就怎麼取的呢?

直接在比較器中返回1就行了,這樣可以保證後進來的元素經比較後總是大於前面的元素,如果要倒序取出的話,直接返回一個-1即可。

public class ComparatorsByName implements Comparator {
	public int compare(Object o1,Object o2) {
		return 1;
	}
}

16-13,TreeSet練習

需求:按照字串的長度排序。

public class TreeSetTest {
	public static void main(String[] args) {
		TreeSet ts = new TreeSet(new ComparatorsByLength());
		ts.add("aaaaaa");
		ts.add("zz");
		ts.add("nabq");
		ts.add("cba");
		ts.add("abc");
		Iterator it = ts.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
	}
}
//比較器
public class ComparatorsByLength implements Comparator {
	public int compare(Object o1,Object o2) {
		String s1 = (String)o1;
		String s2 = (String)o2;
		int temp = s1.length() - s2.length();
		return temp == 0 ? s1.compareTo(s2) : temp;
	}
}