[2014-08-08]JAVA筆記_Collection(集合)

一、 集合框架中的接口


 所謂框架就是一個類庫的集合。集合框架就是一個用來表示和操作集合的統一的架構,包含了實現集合的接口與類。


二、List接口主要有2個實現類:ArrayList、LinkedList

·ArrayList

        1. ArrayList 底層採用數組實現,但是用不帶參數的構造方法生產 ArrayList 對象時,實際上會在底層生成一個長度爲 10 的 Object 類型的數組。 如果增加的元素超過10個,那麼ArrayList底層會新生成一個數組,長度爲原數組的1.5倍+1,然後將原數組的內容複製到新數組當中,並且後續增加的內容都會放到新數組當中。當新數組無法容納增加的元素時,重複該過程。

         2. 對於 ArrayList 元素的刪除操作,需要將被刪除元素的後續元素向前移動,代價比較高。

         3. 集合當中只能放置對象的引用,無法放置原生數據類型,我們需要使用原生數據類型的包裝類才能加入到集合當中。

         4. 集合當中放置的都是 Object 類型,因此取出來的也是 Object 類型,那麼必須要使用強制類型轉換將其轉換爲真正的類型(放置進去的類型)。


·LinkedList

          1. LinkedList 底層主要由雙向鏈表實現。

          2. 添加數據:LinkedList list = new LinkedList();  list.add("elements");

          當向 ArrayList 添加啊一個對象時,實際上就是將該對象放置到了 ArrayList 底層所維護的數組當中;當向 LinkedList 中添加一個對象時,實際上 LinkedList 內部會生成一個Entry 對象,該 Entry 對象的結構爲:

 // Entry類的僞代碼
  Entry{
  	Entry prevoius;
  	Object element;
  	Entry next;
  }
  
  Entry entry = new Entry();
  entry.element = "elements";
  list.add(entry);
 
其中的 Object 類型的元素 element 就是我們向 LinkedList 中所添加的元素,然後 Entry 又構造好了向前與向後的引用 previous、next,最後將生成的這個Entry 對象加入到了鏈表當中。換句話說,LinkedList 中所維護的是一個個的Entry 對象。


· 關於ArrayList 與 LinkedList 的比較分析:

           a) ArrayList 底層採用數組實現,LInkedList 底層採用雙向鏈表實現。

           b) 當執行插入或者刪除操作時,採用 LinkedList 比較好。

           c) 當執行搜索操作時,採用 ArrayLIst 比較好。


三、 Set 接口

Set的特點:1. 是無序的、沒有重複的。

set接口實現類常用的是HashSet。

package com.bob.set;

import java.util.HashSet;

public class SetTest2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		HashSet set = new HashSet();
		
/*		
		set.add(new People("zhangsan"));
		set.add(new People("lisi"));
		set.add(new People("zhangsan"));

		People p1 = new People("zhangsan");
		
		set.add(p1);
		set.add(p1);
*/
		
		String s1 = new String("a");
		String s2 = new String("a");
		
		set.add(s1);
		set.add(s2);
		
		System.out.println(set);
	}

}

class People{
	String name;
	
	public People(String name){
		this.name = name;
	}
}

1. 關於 Object 類的 equals 方法的特點(Object 的 equals 方法是比較兩個對象是否相等)。

在一個非空的對象引用上它實現一個相等性的關係,它滿足以下條件:

       a)  自反性:x.equals(x)應該返回 true
       b)  對稱性:x.equals(y)爲 true,那麼 y.equals(x)也爲 true。
       c)  傳遞性:x.equals(y)爲  true 並且 y.equals(z)爲 true,那麼 x.equals(z)也應該爲 true。
       d)  一致性:x.equals(y)的第一次調用爲 true,那麼 x.equals(y)的第二次、第三次、第 n次調用也應該爲 true,前提條件是在比較之間沒有修改 x 也沒有修改 y。
       e)  對於非空引用 x,x.equals(null)返回 false。

注意:通常重寫 equals() 方法的時候我們也會 重寫 hashCode() 方法,這樣以保證對hashCode()方法的一般性契約,表明相等的對象也有相同的hash Code


2. Object 類中的 hashCode() 方法:

      定義: public int hashCode();    返回一個對象的 hash code  值。這個方法是爲了更好的支持 java.util.Hashtable 這個類。

關於 Object 類的 hashCode()方法的特點

       a)  在 Java 應用的一次執行過程當中,對於同一個對象的 hashCode 方法的多次調用,他們應該返回同樣的值(前提是該對象的信息沒有發生變化) 。
       b)  對於兩個對象來說,如果使用 equals 方法比較返回 true,那麼這兩個對象的 hashCode值一定是相同的。
       c)  對於兩個對象來說,如果使用 equals 方法比較返回 false,那麼這兩個對象的 hashCode值不要求一定不同(可以相同,可以不同) ,但是如果不同則可以提高應用的性能。
       d)  對於 Object 類來說,不同的 Object 對象的 hashCode 值是不同的(Object 類的 hashCode值表示的是對象的地址) 。


3. 當使用 HashSet 時, hashCode()方法就會得到調用,判斷已經存儲在集合中的對象的hash code 值是否與增加的對象的 hash code 值一致;如果不一致,直接加進去;如果一致,再進行 equals 方法的比較,equals 方法如果返回 true,表示對象已經加進去了,就不會再增加新的對象,否則加進去。(所以當調用set 的 add() 方法的時候底層是通過 hashCode()  和  equals() 方法共同協作完成的。)


4. 如果我們重寫 equals 方法,那麼也要重寫 hashCode 方法,反之亦然。


//覆寫hashCode()和equals()方法實現比較人名相同就不添加對象
package com.bob.set;

import java.util.HashSet;

public class SetTest3 {

	public static void main(String[] args) {
		HashSet set = new HashSet();
		
		Student s1 = new Student("zhangsan");
		Student s2 = new Student("zhangsan");

		set.add(s1);
		set.add(s2);
		
		System.out.println(set);
	}

}

class Student{
	String name;
	
	public Student(String name){
		this.name = name;
	}
	
	//override hashCode method
	public int hashCode(){
		return this.name.hashCode();
	}
	
	//override equals method
	public boolean equals(Object obj){
		if(this == obj){
			return true;
		}
		
		if(null != obj && obj instanceof Student){
			Student s = (Student)obj;
			
			if(name.equals(s.name)){
				return true;
			}
		}
		return false;
	}
}
//也可使用eclipse工具自動生成這2個方法。Source-->generate hashCode() and equals();

以上例子中取出來的都是[....]對象,如果想要取出對象的屬性需要使用 HashSet 中的 iterator() 方法,此方法返回類型爲 iterator 接口(可以使用子類實現接口方式返回此類型) 。iterator() 方法定義在 Iterable 接口中,因爲Collection 接口是 iterable 的子接口,所以所有類集中都包含iterator()方法。

package com.bob.set;

import java.util.HashSet;
import java.util.Iterator;

public class IteratorTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		HashSet set = new HashSet();
		
		set.add("a");
		set.add("b");
		set.add("c");
		set.add("d");

		
	//	使用 Iterator必須先調用HashSet類的iterator()方法返回一個Iterator對象
		
//		Iterator iter = set.iterator();
//		
//		while(iter.hasNext()){
//			String value = (String)iter.next();
//			System.out.println(value);
//		}
		
		for(Iterator iter = set.iterator(); iter.hasNext();){
			String value = (String)iter.next();
			System.out.println(value);
		}

	//	對於Set一般採取迭代器(遍歷)方式取得元素。
	//	List也可以通過這種方式取得,但是一般list直接使用下標操縱更加方便。

	}

}

5. Set的子接口:SortedSet

 SortedSet 中主要常用類:TreeSet


//將對象排序
package com.bob.set;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetTest2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		TreeSet set = new TreeSet(new PersonComparator()); 	// 按照此種實例方法規則排序

		Person p1 = new Person(10);
		Person p2 = new Person(20);
		Person p3 = new Person(30);
		Person p4 = new Person(40);

		set.add(p1);
		set.add(p2); // 向TreeSet添加對象TreeSet會自動跟裏面已有對象進行比較
		set.add(p3);
		set.add(p4);

		for (Iterator iter = set.iterator(); iter.hasNext();) {
			Person p = (Person) iter.next();
			System.out.println(p.score);
		}

	}

}

class Person {
	int score;

	public Person(int score) {
		this.score = score;
	}

	public String toString() {
		return String.valueOf(this.score);
	}
}

// 自定義比較規則
class PersonComparator implements Comparator {

	@Override
	public int compare(Object o1, Object o2) {
		// compare()方法返回值意思:
		// <0,arg0 < arg1。 arg1在arg0前面
		// =0, arg0 == arg1
		// <0, arg0 > arg1
		Person p1 = (Person) o1; 	// 集合中放置的是什麼類型就轉換成什麼類型
		Person p2 = (Person) o2;

		return -(p1.score - p2.score); // 根據返回值來進行排序
	}
}



Collections 靜態類

package com.bob.set;

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;

public class CollectionsTest {
	public static void main(String[] args) {
		LinkedList list = new LinkedList();
		
		list.add(new Integer(-3));
		list.add(new Integer(-1));
		list.add(new Integer(-9));
		list.add(new Integer(3));
		list.add(new Integer(13));
		
		//使用Collections類中的reverseOrder()方法進行反序
		Comparator cmr = Collections.reverseOrder();
		
		Collections.sort(list, cmr);
		
		for(Iterator iter = list.iterator(); iter.hasNext();){
			System.out.println(iter.next() + " ");
		}
		
		System.out.println("#####################");
		
		//將集合打亂
		Collections.shuffle(list);
		for(Iterator iter = list.iterator(); iter.hasNext();){
			System.out.println(iter.next() + " ");
		}
		
		//打印最大數和最小數
		System.out.println("minimum value:" + Collections.min(list));
		System.out.println("maximum value:" + Collections.max(list));
	}
}

四 、 Map(映射)

      1. map是一個對象,它會將鍵映射到它的值上。一個 map 不能包含重複的鍵,一個鍵最多映射一個值。(值可以是重複的)

      2. Map 的 keySet() 方法返回 Key 的集合, 因爲 Map 的鍵是不能重複的的,因此 keySet()方法的返回類型是 Set;而 Map 的值是可以重複的,因此 values() 方法的返回類型是 Collection, 可以容納重複的元素。

              keySet() 方法返回所有鍵的集合。返回一個 Set 類型,因爲 Set 裏面的元素是不能重複的。

              values() 方法返回一個 Collection 類型,因爲 Collection 是可以重複的。

// put() get() keySet()
package com.bob.set;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

public class MapTest3 {

	public static void main(String[] args) {
		HashMap map = new HashMap();
		
		map.put("a", "aa");
		map.put("b", "bb");
		map.put("c", "cc");
		map.put("d", "dd");
		map.put("e", "ee");

		//使用keySet()獲取所有key的集合,遍歷所有key,或者key對於的值
		Set set = map.keySet();
		
		for(Iterator iter = set.iterator() ; iter.hasNext(); ){
			String key = (String)iter.next();
			String value = (String)map.get(key);
			
			System.out.println(key + "=" + value);
		}

	}

}

//使用entrySet()方式實現。Map接口中的成員變量Map.Entry類型
package com.bob.set;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MapTest5 {

	public static void main(String[] args) {
		HashMap map = new HashMap();
		
		map.put("a", "aa");
		map.put("b", "bb");
		map.put("c", "dd");
		map.put("c", "dd");

		//獲取entry對象,entry對象裏面包含了key和value一對的映射視圖
		Set set = map.entrySet();
		//map.entrySet返回的類型是Map.Entry
		for(Iterator iter = set.iterator(); iter.hasNext(); ){
			Map.Entry entry = (Map.Entry)iter.next();
			String key = (String)entry.getKey();
			String value = (String)entry.getValue();
			
			System.out.println(key + " : " + value);
		}
		
	}

}

3. TreeMap

        TreeMap類不僅實現了Map接口,還實現了Map接口的子接口java.util.SortedMap。


五、HashSet 與 HashMap 源代碼

         1. HashSet 底層是用 HashMap 實現的。當使用 add() 方法將對象添加到 Set 當中時, 實際上是將該對象作爲底層所維護的 Map 對象的 key,而 value 則都是同一個 Object 對象(該對象我們用不上);

         2. HashMap 底層會維護一個數組,我們向 HashMap 中放置的對象實際上是存儲在該數組中。

         3. 當向 HashMap 中 put 一對鍵值時,它會根據 key 的 hashCode 值計算出一個位置,該位置就是此對象準備往數組中存放的位置。如果該位置沒有對象存在,就將此對象直接放進數組中; 如果該位置已經有對象存在了,則順着此存在的對象的鏈開始尋找(Entry 類有一個 Entry 類型的 next 成員變量,指向了該對象的下一個對象),如果該鏈上有對象的話,在去使用 equals 方法進行比較,如果對此鏈上的某個對象的 equals 方法比較爲 false,則將該對象放到數組當中,然後將數組中該位置以前存在的那個對象鏈接到此對象的後面(根據操作系統原理,當前被使用的元素,在不久的將來更有可能會被使用,所以替換了之前的位置)。

HashMap 的內存實現佈局












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章