Java 集合詳解

1、集合的由來html

  一般,咱們的程序須要根據程序運行時才知道建立多少個對象。但若非程序運行,程序開發階段,咱們根本不知道到底須要多少個數量的對象,甚至不知道它的準確類型。爲了知足這些常規的編程須要,咱們要求能在任什麼時候候,任何地點建立任意數量的對象,而這些對象用什麼來容納呢?咱們首先想到了數組,可是數組只能放統一類型的數據,並且其長度是固定的,那怎麼辦呢?集合便應運而生了!java

 

爲了對集合有個更加深刻的瞭解,能夠看個人這一篇文章:用 Java 數組來實現 ArrayList 集合 http://www.cnblogs.com/ysocean/p/6812674.html算法

 

2、集合是什麼?編程

  Java集合類存放於 java.util 包中,是一個用來存放對象的容器。數組

注意:①、集合只能存放對象。好比你存一個 int 型數據 1放入集合中,其實它是自動轉換成 Integer 類後存入的,Java中每一種基本類型都有對應的引用類型。安全

   ②、集合存放的是多個對象的引用,對象自己仍是放在堆內存中。數據結構

   ③、集合能夠存放不一樣類型,不限數量的數據類型。框架

 

3、Java 集合框架圖ide

此圖來源於:http://blog.csdn.net/u010887744/article/details/50575735性能

大圖能夠點此訪問:http://img.blog.csdn.net/20160124221843905

 

  發現一個特色,上述全部的集合類,除了 map 系列的集合,即左邊集合都實現了 Iterator 接口,這是一個用於遍歷集合中元素的接口,主要hashNext(),next(),remove()三種方法。它的一個子接口 ListIterator 在它的基礎上又添加了三種方法,分別是 add(),previous(),hasPrevious()。也就是說若是實現 Iterator 接口,那麼在遍歷集合中元素的時候,只能日後遍歷,被遍歷後的元素不會再被遍歷到,一般無序集合實現的都是這個接口,好比HashSet;而那些元素有序的集合,實現的通常都是 LinkedIterator接口,實現這個接口的集合能夠雙向遍歷,既能夠經過next()訪問下一個元素,又能夠經過previous()訪問前一個 元素,好比ArrayList。

  還有一個特色就是抽象類的使用。若是要本身實現一個集合類,去實現那些抽象的接口會很是麻煩,工做量很大。這個時候就可使用抽象類,這些抽象類中給咱們提供了許多

現成的實現,咱們只須要根據本身的需求重寫一些方法或者添加一些方法就能夠實現本身須要的集合類,工做量大大下降。

 

4、集合詳解

①、Iterator:迭代器,它是Java集合的頂層接口(不包括 map 系列的集合,Map接口 是 map 系列集合的頂層接口)

  Object next():返回迭代器剛越過的元素的引用,返回值是 Object,須要強制轉換成本身須要的類型

  boolean hasNext():判斷容器內是否還有可供訪問的元素

  void remove():刪除迭代器剛越過的元素

因此除了 map 系列的集合,咱們都能經過迭代器來對集合中的元素進行遍歷。

注意:咱們能夠在源碼中追溯到集合的頂層接口,好比 Collection 接口,能夠看到它繼承的是類 Iterable

那這就得說明一下 Iterator 和 Iterable 的區別:

 Iterable :存在於 java.lang 包中。

    

咱們能夠看到,裏面封裝了 Iterator 接口。因此只要實現了只要實現了Iterable接口的類,就可使用Iterator迭代器了。

 

 Iterator :存在於 java.util 包中。核心的方法next(),hasnext(),remove()。

 這裏咱們引用一個Iterator 的實現類 ArrayList 來看一下迭代器的使用:暫時先無論 List 集合是什麼,只須要看看迭代器的用法就好了

 1         //產生一個 List 集合,典型實現爲 ArrayList。
 2         List list = new ArrayList();
 3         //添加三個元素
 4         list.add("Tom");
 5         list.add("Bob");
 6         list.add("Marry");
 7         //構造 List 的迭代器
 8         Iterator it = list.iterator();
 9         //經過迭代器遍歷元素
10         while(it.hasNext()){
11             Object obj = it.next();
12             System.out.println(obj);
13         }

 

 ②、Collection:List 接口和 Set 接口的父接口

    

  看一下 Collection 集合的使用例子:
 1         //咱們這裏將 ArrayList集合做爲 Collection 的實現類
 2         Collection collection = new ArrayList();
 3         
 4         //添加元素
 5         collection.add("Tom");
 6         collection.add("Bob");
 7         
 8         //刪除指定元素
 9         collection.remove("Tom");
10         
11         //刪除全部元素
12         Collection c = new ArrayList();
13         c.add("Bob");
14         collection.removeAll(c);
15         
16         //檢測是否存在某個元素
17         collection.contains("Tom");
18         
19         //判斷是否爲空
20         collection.isEmpty();
21         
22         //利用加強for循環遍歷集合
23         for(Object obj : collection){
24             System.out.println(obj);
25         }
26         //利用迭代器 Iterator
27         Iterator iterator = collection.iterator();
28         while(iterator.hasNext()){
29             Object obj = iterator.next();
30             System.out.println(obj);
31         }

 

 ③、List :有序,能夠重複的集合。

  

因爲 List 接口是繼承於 Collection 接口,因此基本的方法如上所示。

一、List 接口的三個典型實現:

  ①、List list1 = new ArrayList();

    底層數據結構是數組,查詢快,增刪慢;線程不安全,效率高

   ②、List list2 = new Vector();

    底層數據結構是數組,查詢快,增刪慢;線程安全,效率低,幾乎已經淘汰了這個集合

   ③、List list3 = new LinkedList();

    底層數據結構是鏈表,查詢慢,增刪快;線程不安全,效率高

 

 怎麼記呢?咱們能夠想象:

  數組就像身上編了號站成一排的人,要找第10我的很容易,根據人身上的編號很快就能找到。但插入、刪除慢,要望某個位置插入或刪除一我的時,後面的人身上的編號都要變。固然,加入或刪除的人始終末尾的也快。

  鏈表就像手牽着手站成一圈的人,要找第10我的不容易,必須從第一我的一個個數過去。但插入、刪除快。插入時只要解開兩我的的手,並從新牽上新加進來的人的手就能夠。刪除同樣的道理。

 

 二、除此以外,List 接口遍歷還可使用普通 for 循環進行遍歷,指定位置添加元素,替換元素等等。

 1      //產生一個 List 集合,典型實現爲 ArrayList
 2         List list = new ArrayList();
 3         //添加三個元素
 4         list.add("Tom");
 5         list.add("Bob");
 6         list.add("Marry");
 7         //構造 List 的迭代器
 8         Iterator it = list.iterator();
 9         //經過迭代器遍歷元素
10         while(it.hasNext()){
11             Object obj = it.next();
12             //System.out.println(obj);
13         }
14         
15         //在指定地方添加元素
16         list.add(2, 0);
17          
18         //在指定地方替換元素
19         list.set(2, 1);
20          
21         //得到指定對象的索引
22         int i=list.indexOf(1);
23         System.out.println("索引爲:"+i);
24         
25         //遍歷:普通for循環
26         for(int j=0;j<list.size();j++){
27              System.out.println(list.get(j));
28         }

 

 ④、Set:典型實現 HashSet()是一個無序,不可重複的集合

 一、Set hashSet = new HashSet();

  ①、HashSet:不能保證元素的順序;不可重複;不是線程安全的;集合元素能夠爲 NULL;

  ②、其底層實際上是一個數組,存在的意義是加快查詢速度。咱們知道在通常的數組中,元素在數組中的索引位置是隨機的,元素的取值和元素的位置之間不存在肯定的關係,所以,在數組中查找特定的值時,須要把查找值和一系列的元素進行比較,此時的查詢效率依賴於查找過程當中比較的次數。而 HashSet 集合底層數組的索引和值有一個肯定的關係:index=hash(value),那麼只須要調用這個公式,就能快速的找到元素或者索引。

  ③、對於 HashSet: 若是兩個對象經過 equals() 方法返回 true,這兩個對象的 hashCode 值也應該相同。
     一、當向HashSet集合中存入一個元素時,HashSet會先調用該對象的hashCode()方法來獲得該對象的hashCode值,而後根據hashCode值決定該對象在HashSet中的存儲位置
      1.一、若是 hashCode 值不一樣,直接把該元素存儲到 hashCode() 指定的位置
      1.二、若是 hashCode 值相同,那麼會繼續判斷該元素和集合對象的 equals() 做比較
          1.2.一、hashCode 相同,equals 爲 true,則視爲同一個對象,不保存在 hashSet()中
          1.2.二、hashCode 相同,equals 爲 false,則存儲在以前對象同槽位的鏈表上,這很是麻煩,咱們應該約束這種狀況,即保證:若是兩個對象經過 equals() 方法返回 true,這兩個對象的 hashCode 值也應該相同。
 
注意:每個存儲到 哈希 表中的對象,都得提供 hashCode() 和 equals() 方法的實現,用來判斷是不是同一個對象
   對於 HashSet 集合,咱們要保證若是兩個對象經過 equals() 方法返回 true,這兩個對象的 hashCode 值也應該相同。
 
  
常見的 hashCode()算法:
 
 
二、Set linkedHashSet = new LinkedHashSet();
  ①、不能夠重複,有序
  由於底層採用 鏈表 和 哈希表的算法。鏈表保證元素的添加順序,哈希表保證元素的惟一性
 
 
三、Set treeSet = new TreeSet();
  TreeSet:有序;不可重複,底層使用 紅黑樹算法,擅長於範圍查詢。
  *  若是使用 TreeSet() 無參數的構造器建立一個 TreeSet 對象, 則要求放入其中的元素的類必須實現 Comparable 接口因此, 在其中不能放入 null 元素
 
     *   必須放入一樣類的對象.(默認會進行排序) 不然可能會發生類型轉換異常.咱們可使用泛型來進行限制
 
Set treeSet = new TreeSet();
		treeSet.add(1);  //添加一個 Integer 類型的數據
		treeSet.add("a");	//添加一個 String 類型的數據
		System.out.println(treeSet);  //會報類型轉換異常的錯誤

  

   * 自動排序:添加自定義對象的時候,必需要實現 Comparable 接口,並要覆蓋 compareTo(Object obj) 方法來自定義比較規則

    若是 this > obj,返回正數 1

    若是 this < obj,返回負數 -1

    若是 this = obj,返回 0 ,則認爲這兩個對象相等

 

           *  兩個對象經過 Comparable 接口 compareTo(Object obj) 方法的返回值來比較大小, 並進行升序排列
           

 

 
  
   *  定製排序: 建立 TreeSet 對象時, 傳入 Comparator 接口的實現類. 要求: Comparator 接口的 compare 方法的返回值和 兩個元素的 equals() 方法具備一致的返回值  
 
public class TreeSetTest {
	public static void main(String[] args) {
		Person p1 = new Person(1);
		Person p2 = new Person(2);
		Person p3 = new Person(3);
		
		Set<Person> set = new TreeSet<>(new Person());
		set.add(p1);
		set.add(p2);
		set.add(p3);
		System.out.println(set);  //結果爲[1, 2, 3]
	}

}

class Person implements Comparator<Person>{
	public int age;
	public Person(){}
	public Person(int age){
		this.age = age;
	}
	@Override
	/***
	 * 根據年齡大小進行排序
	 */
	public int compare(Person o1, Person o2) {
		// TODO Auto-generated method stub
		if(o1.age > o2.age){
			return 1;
		}else if(o1.age < o2.age){
			return -1;
		}else{
			return 0;
		}
	}
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return ""+this.age;
	}
}

  

 
     *  當須要把一個對象放入 TreeSet 中,重寫該對象對應的 equals() 方法時,應保證該方法與 compareTo(Object obj) 方法有一致的結果
   
    
以上三個 Set 接口的實現類比較:
  共同點:一、都不容許元素重複
      二、都不是線程安全的類,解決辦法:Set set = Collections.synchronizedSet(set 對象)
 
 
  不一樣點:
    HashSet:不保證元素的添加順序,底層採用 哈希表算法,查詢效率高。判斷兩個元素是否相等,equals() 方法返回 true,hashCode() 值相等。即要求存入 HashSet 中的元素要覆蓋 equals() 方法和 hashCode()方法
 
    LinkedHashSet:HashSet 的子類,底層採用了 哈希表算法以及 鏈表算法,既保證了元素的添加順序,也保證了查詢效率。可是總體性能要低於 HashSet    
 
    TreeSet:不保證元素的添加順序,可是會對集合中的元素進行排序。底層採用 紅-黑 樹算法(樹結構比較適合範圍查詢)
 
 

  

 

  ⑤、Map:key-value 的鍵值對,key 不容許重複,value 能夠

  一、嚴格來講 Map 並非一個集合,而是兩個集合之間 的映射關係。

    二、這兩個集合沒每一條數據經過映射關係,咱們能夠當作是一條數據。即 Entry(key,value)。Map 能夠當作是由多個 Entry 組成。

    三、由於 Map 集合即沒有實現於 Collection 接口,也沒有實現 Iterable 接口,因此不能對 Map 集合進行 for-each 遍歷。

            

            

 

Map<String,Object> hashMap = new HashMap<>();
		//添加元素到 Map 中
		hashMap.put("key1", "value1");
		hashMap.put("key2", "value2");
		hashMap.put("key3", "value3");
		hashMap.put("key4", "value4");
		hashMap.put("key5", "value5");
		
		//刪除 Map 中的元素,經過 key 的值
		hashMap.remove("key1");
		
		//經過 get(key) 獲得 Map 中的value
		Object str1 = hashMap.get("key1");
		
		//能夠經過 添加 方法來修改 Map 中的元素
		hashMap.put("key2", "修改 key2 的 Value");
		
		//經過 map.values() 方法獲得 Map 中的 value 集合
		Collection<Object> value = hashMap.values();
		for(Object obj : value){
			//System.out.println(obj);
		}
		
		//經過 map.keySet() 獲得 Map 的key 的集合,而後 經過 get(key) 獲得 Value
		Set<String> set = hashMap.keySet();
		for(String str : set){
			Object obj = hashMap.get(str);
			//System.out.println(str+"="+obj);
		}
		
		//經過 Map.entrySet() 獲得 Map 的 Entry集合,而後遍歷
		Set<Map.Entry<String, Object>> entrys = hashMap.entrySet();
		for(Map.Entry<String, Object> entry: entrys){
			String key = entry.getKey();
			Object value2 = entry.getValue();
			System.out.println(key+"="+value2);
		}
		
		System.out.println(hashMap);

  Map 的經常使用實現類:

        

 

 

 

 

 

  ⑥、Map 和 Set 集合的關係

    一、都有幾個類型的集合。HashMap 和 HashSet ,都採 哈希表算法;TreeMap 和 TreeSet 都採用 紅-黑樹算法;LinkedHashMap 和 LinkedHashSet 都採用 哈希表算法和紅-黑樹算法。

    二、分析 Set 的底層源碼,咱們能夠看到,Set 集合 就是 由 Map 集合的 Key 組成。

        

相關文章
相關標籤/搜索