黑馬程序員_Java中的集合

                    ----------------------android培訓java培訓、期待與您交流! ----------------------

一、什麼是集合?

集合是一個容器,是用來存儲對象的。
爲什麼會有集合?
在面向對象的語言當中,一個事物用一個對應的實例對象進行描述,實例對象有很多,爲了方便操作多個對象,需要將這些對象存儲起來,
而集合就是一個常用的存儲對象的工具。
數組和集合的區別:
集合存儲對象,長度可以改變,存儲的對象的類型也可以不同。集合只能操作對象
數組也可以存儲對象,但長度是固定的,存儲的對象的類型也不許相同。數組還可以操作基本數據類型

集合容器有很多種,爲什麼?
不同容器存儲對象的方式(數據結構)不同。就象,ArrayList在內存中的存儲是一個數組,LinkedList是一個鏈表,而HashSet是一個哈希表
這時,對對象進行不同的操作,就要使用不同的容器。
集合中存放的不是對象實體,而是對象的引用(地址)。要獲取集合中的元素,必須通過迭代器迭代。
集合中的主要集合都是不同步的,真要用到多線程時,自己加鎖太麻煩,Collections提供了方法讓集合安全。
二、集合體系。
1.Collection集合體系--->存儲多個 獨立對象的容器
Collection<E> 最常用的是:ArrayList、LinkedList、HashSet
|--List:元素是有序的,元素可以重複。因爲該集合體繫有索引。
面試 |--ArrayList: 底層使用數組數據結構。特點:查詢速度快,增刪速度慢。線程不同步。
|--LinkedList:底層使用鏈表數據結構。特點:增刪速度很快,查詢速度稍慢。
|--Vector:   底層使用數組數據結構。線程同步的,被ArrayList替代,即使多線程也不使用Vector,
可以自己給ArrayList加鎖,Java也提供了方法給ArrayList加鎖。
|--Set:元素是無序的(存入和取出的順序不一定一致),元素不可以重複。
|--HashSet:底層數據結構是哈希表。線程不同步。保證元素唯一性使用方法hashCode和equals
|--TreeSet:底層數據結構是二叉樹。可以對集合中的元素排序。保證元素唯一用compareTo方法返回0

1.1使用注意:
1.List中,比較元素使用方法equals方法。所以List存儲不重複的自定義對象時,自定義類要重寫equals方法。
2.什麼時候使用ArrayList和LinkedList?
對集合元素的操作,涉及到頻繁的增刪,用LinkedList
 涉及到增刪但不頻繁,用ArrayList
 涉及到了增刪,也涉及到查詢,建議用ArrayList。因爲頻繁的增刪操作不常見,經常是做查詢。
3.HashSet,如何保證元素的唯一性?
通過元素的兩個方法,equals和hashCode來實現的。
會先比較hashCode值,相等時纔會去比較equals。查詢和刪除也是使用這兩個方法。
4.ArrayList和HashSet的區別
面試題 HashCode的作用?--->靈活應變:Java中有內存泄露嗎?爲什麼?
/*HashSet的存儲機制中,根據哈希值分成不同的區域。
* 1.存儲時,先用hashCode方法計算出對象的哈希值,
* 根據計算出的哈希值再到相對應的區域中逐一取出已經存儲的對象和要存儲的對象用equals方法比較內容是否相同。
* 若內容相同就不存儲了,而不是覆蓋;內容不同,就存儲該對象。

* 查詢時,也是先用hashCode方法計算出要查詢的對象的哈希值,根據計算出的值到相應區域用equals比較查找。

* 2.對於像rp1、rp2這種,同一類型的內容不同的兩個對象,程序員應該讓他們在內存中只存儲一個,所以要重寫hashCode方法,
* 但是當存儲機制不是HashSet時,也就無所謂了,因爲不會用到hashCode方法

* 3.當一個對象已經被存儲到HashSet集合當中時,就不要再修改對象中參與計算哈希值的字段了,因爲修改以後對象的哈希值變了,
* 在這種情況下,當用contains檢索該對象時,會到新的哈希值對應的區域中查找,這就無法發現在舊區域中的對象,用remove刪除
* 時,就會發生內存泄露*/
/*ArrayList的底層是數組結構的,存儲時會插入到數組的尾部,查找時會從數組的開始依次向後查找。*/
5.TreeSet如何實現對元素的排序?
有兩種方法:
方法一:讓元素自身具有比較性。即,元素所屬類,實現接口Comparable,覆蓋int compareTo(Object)方法
方法二:讓容器自身具有比較性。給集合添加一個比較器,在集合初始化時傳入--->自定義一個類,實現接口Comparator,覆蓋compare(obj1,obj2)方法
當元素自身不具備比較性,或者已有的比較性不是自己想要的,這時,使用方法二。
6.泛型。解決安全問題的一個安全機制。在集合框架中很常見。
泛型的引入:集合中可以存儲不同類型的對象,當獲取對象時,若進行強制類型轉換就會拋出ClassCastException異常,而這個異常在運行時期
才能被發現,這不方便於程序員解決問題。爲了將問題轉換到編譯時期,使用泛型來限定集合中只能存入某一類型的對象。
好處:將運行時期的問題,轉移到了編譯時期; 避免了強制轉換的麻煩。
格式:<>內寫上要存儲的對象類型。ArrayList<String> al=new ArrayList<String>();
 泛型寫在類上時,在整個類中有效。
 泛型寫在方法上(放在返回值類型前),可以讓不同方法操作不同類型的數據,而數據的類型還不確定。
  注意,靜態方法的泛型不可以和類上的相同。如果靜態方法操作的數據類型也不確定,可以在靜態方法上,定義獨立的泛型。
 泛型寫在接口上  
 
1.2.各集合中的方法
1.2.1 Collection 該層次結構的根接口。該類容器存儲的多個對象表示一組對象
Collection中的方法:(增刪改查)
添加:boolean add(E e);
 boolean addAll(Collection<? extends E> c);
刪除:boolean remove(Object o);清除一個對象
 boolean removeAll(Collection<?> c);刪除一堆元素,並不是全部
 void clear();清空
判斷:boolean isEmpty();-->相當於collection.size==0
 boolean contains(Object o);
 boolean containsAll(Collection<?> c);
 boolean equals(Object o);比較此 collection 與指定對象是否相等。
獲取:int size();元素個數
 Object[] toArray() 將此 collection 轉換成數組。
 boolean retainAll(Collection<?> c);取交集,且此集合內容也會改變,只存交集。
 Iterator iterator();獲取集合中的迭代器
獲取Collection集合中的元素:
要獲取集合中的元素,只能通過迭代器。步驟:
1.獲取集合內部的迭代器(Iterator接口)。
2.利用迭代器中的方法,獲取元素。
Iterator迭代器中的三個方法:boolean hasNext();判斷容器中是否還有元素
E next():獲取迭代的下一個元素。
void remove():移除迭代器返回的最後一個元素
代碼實現:第一種:Iterator it=arrayList.iterator();
 while(it.hasNext()){
 System.out.println(it.next());
 } 
 第二種:for(Iterator it=arrayList.iterator();it.hasNext(); ){
  System.out.println(it.next());
  }
迭代器:就是獲取集合中元素的方式。每個集合容器中都有自己的迭代器,這樣就可以直接訪問集合內部的元素。
實際上,是在類中定義了一個內部類Itr實現了Iterator接口,然後在iterator方法中返回Itr的實例對象
         
1.2.2 List
List中的特有方法:凡是可以操作角標的方法都是該體系特有的方法。
增:boolean add(int index,E element);
boolean addAll(int index,Collection<? extends E> c);
刪:E remove(int index);
改:E set(int index,E element);
查:E get(int index);獲取指定位置上的元素
List<E> subList(int fromIndex,int toIndex);返回集合中指定的 fromIndex(包括 )和 toIndex(不包括)之間的部分視圖
ListIterator<E> listIterator()返回List的迭代器,List中特有的迭代器。
int indexOf(Object o);
int lastIndexOf(Object o);
List集合特有的迭代器ListIterator:是Iterator的子接口
在迭代時,不可通過集合對象中的方法操作元素。因爲會發生ConcurrentModificationException異常。
所以,在迭代時,想要操作元素只能用迭代器中的方法,可是Iterator中的方法是有限的,只能對元素進行判斷、取出、刪除操作。
要想進行其他操作如添加、修改等,就必須使用迭代器ListIterator。但,該迭代器只能用於List。
ListIterator特有方法:
boolean hasPrevious() 逆向遍歷列表 
E previous() 返回列表中的前一個元素。 
void add(E e) 迭代時添加元素,添加到獲取元素的後面
void set(E e) 迭代時修改元素,修改當前獲取到的元素
int nextIndex() 
int previousIndex() 
代碼測試:
ListIterator li=al1.listIterator();
while(li.hasNext()){
Object obj=li.next();
if(obj.equals("java02")){
//al1.remove(obj);//編譯失敗。在迭代時,不可通過集合對象的方法操作集合中的元素。
//it.remove();//Iterator迭代器只能進行刪除
li.remove();//刪除之後,就不能用set、add方法
/* li.set("j03");
li.add("aaa");*/
}
}    
1.2.3 Vector
 Vector的方法:凡是帶有element的都是特有方法
  Enumeration<E> elements();獲取集合的枚舉,相當於迭代器。
1.2.4 LinkedList
LinkedList特有方法:
addFirst(); addLast();
getFirst(); getLast(); 獲取元素但不刪除。如果集合中沒有該元素,會拋出異常NoSuchElementException
removeFirst(); removeLast(); 獲取元素並刪除。如果集合中沒有該元素,會拋出異常NoSuchElementException
JDK1.6中出現了替代方法:
offerFirst(); offerLast();添加
peekFirst(); peekLast(); 獲取。 如果集合中沒有該元素,返回null。
pollFirst(); pollLast(); 獲取並刪除。如果集合中沒有該元素,返回null。
1.2.5 Set.底層使用了Map
Set中的方法和Collection一樣。

2.Map集合體系--->存儲多個 映射(兩個對象關聯)的容器
Map 特點:將鍵映射到值的對象.鍵和值一一映射,鍵不能重複。最常用
|--HashTable:底層數據結構是哈希表。不能存入null鍵null值。線程同步的。jdk1.1  存入null鍵null值。線程同步的。jdk1.1
|--HashMap:  底層數據結構是哈希表。可以存入null鍵null值。線程不同步的。jdk1.2。將HashTable替代。效率高
|--LinkedHashMap
|--TreeMap:  底層數據結構是二叉樹。線程不同步。可以用於給Map集合中的鍵進行排序。


|--Properties
|--AbstractMap

2.1什麼時候使用Map集合?
當數據之間存在着映射關係時,就要先想到Map集合。

2.2 各集合方法
2.2.1 Map。也是該層次的根接口
1.增加。
V put(K key,V value);存入一個鍵值對。如果鍵已經存在,就將舊值覆蓋。該方法返回的是key之前對應的值,所以,第一次存入時返回null。
putAll(Map<? extends K,? extends V> m)
2.刪除。
clear();
remove(Object key);根據鍵刪除一個映射。注意,不能根據值刪除。
3.判斷。
isEmpty();
containsKey(Object key);
containsValue(Object value);
4.獲取。
V get(Object key);根據鍵獲取對應的值。注意:不能根據值獲取對應的鍵。在HashMap中,可以根據null鍵獲取到值,而HashTable不可以有null鍵。
只能根據一個鍵獲取一個值,不能獲取所有值。若有什麼鍵都不知道怎麼獲取呢?
Set<K> keySet();將Map中所有的鍵存入到Set集合中。
Set<Map.Entry<K,V>> entrySet();將Map集合中的所有映射關係存入到Set中。

獲取Map集合中的元素:Map中沒有提供獲取迭代器的方法。所以。將Map集合轉換成Set,然後迭代取出。有兩種方法:
方法一、將Map中所有的鍵存入到Set集合中,再根據get方法,獲取每一個鍵對應的值。
方法二、將Map集合中的所有映射關係存入到Set中。再用映射關係的方法getValue、getKey獲取鍵和值。
這個映射關係的類型是Map.Entry。其實,Entry也是一個接口,它是Map接口中的一個內部接口。
Map.Entry接口中的方法: getValue()
getKey();
V setValue(V vulue);將Map集合中的所有映射關係存入到Set中。
hashCode(); equals();

三、Collections 操作集合的工具類
引入Collections:List集合允許元素重複,但不能排序;Set集合能實現排序但不能元素重複。如果,既要重複又要排序。怎麼辦?
Java提供了工具類Collections,專用來操作集合的,可以實現這一功能。
Collections和Collection的區別?
Collection是Collection集合體系的根接口。Collections是專用來操作集合的工具類。
Collections類中的方法:
特點:該類中的方法都是靜態的,所以泛型都要定義在方法上。

1.對集合排序。只能對List集合排序。
 static <T extends Comparable<? super T>> void sort(List<T> list):對List集合排序,List集合中的元素自身具備比較性。這就實現了,既要重複又要排序的功能。
  <T extends Comparable<? super T>>:List操作的元素類型T(例如:Student),要具備比較性就必須實現接口Comparable,但接口可能是繼承自父類(Person)的(父類實現了的接口,子類就不用繼承了,實際上也不能),
  排序時先找子類中的compareTo方法,子類中沒有複寫時,再找父類。
 static <T> void sort(List<T> list, Comparator<? super T> c) 根據指定比較器對List進行排序,這時T就不需要具備比較性了。
2.獲取最大值。
 static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 元素自身具有比較性。根據元素的自然順序,返回給定 collection 的最大元素。   
 static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) 根據指定比較器產生的順序,返回給定 collection 的最大元素。
3.二分搜索法查找指定元素
 static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key):使用二分搜索法搜索指定列表,以獲得指定對象。 
 static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
4.替換集合中的元素
 static <T> void fill(List<? super T> list, T obj) 將集合中的所有元素替換成指定元素
 static <T> boolean replaceAll(List<T> list, T oldVal, T newVal):將集合中 指定的所有舊值 替換成 指定的新值。
  5.反轉
   static void reverse(List<?> list) 
   static <T> Comparator<T> reverseOrder() 返回一個比較器,它強行逆轉實現了 Comparable 接口的對象 collection 的自然順序。
    例如:TreeSet<String> ts=new TreeSet<String>(Collections.reverseOrder());將TreeSet中元素的自然順序強行逆轉了 
 static <T> Comparator<T> reverseOrder(Comparator<T> cmp) 返回一個比較器,它強行逆轉指定比較器的順序。
  例如:TreeSet<String> ts=new TreeSet<String>(Collections.reverseOrder(new StrLenComparator()));將按長度排序的比較器強行逆轉。
6.同步集合
 static <T> Collection<T> synchronizedCollection(Collection<T> c) 將指定 collection 轉換成線程同步的並返回
 static <T> List<T> synchronizedList(List<T> list) 
          static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 
          static <T> Set<T> synchronizedSet(Set<T> s) 
  7.交換元素
   static void swap(List<?> list, int i, int j);將兩個指定位置上的元素交換
  8.隨機重排集合元素
   static void shuffle(List<?> list) 使用默認隨機源對指定列表進行置換。----->紙牌遊戲 洗牌 
 static void shuffle(List<?> list, Random rnd) 使用指定的隨機源對指定列表進行置換。 
四、Arrays 操作數組的工具類.
Arrays中的方法:都是靜態的。
1. 將數組變成集合。
  static <T> List<T> asList(T... a); 返回一個固定大小的列表。 Arrays.asList(arr);
  將數組變成集合的好處:可以按照集合的方法來處理數組。如,判斷數組是否包含某個值。數組時得遍歷數組,集合一步搞定
  注意:1.將數組變成集合,不可以使用集合的增刪方法。因爲返回的集合大小是固定的。如果進行了增刪,則拋出異常UnsupportedOperationException
     可以使用的方法:contains()、get()、indexOf()、subList().
    2.如果數組中的元素都是對象。那麼變成集合時,數組中的元素就直接轉換爲集合中的元素。
     如果數組中的元素都是基本類型的。那麼變成集合時,會把該數組作爲集合中的元素。
      例:int[] nums1={1,2,3};  Integer[] nums2={1,2,3};
      List<int[]> li1=Arrays.asList(nums1);//輸出:[[I@2453f89f]
      List<Integer> li2=Arrays.asList(nums2);//輸出:[1, 2, 3]      
2. 將集合變成數組。
  Collection接口中的toArray方法:
    Object[] toArray(); 方法內自動創建了一個數組。
    <T> T[] toArray(T[] a); 存入指定類型的數組  a:存儲此 collection 元素的數組
    注意:1.指定類型的數組要定義多長?
    創建一個剛剛好的最優,即,長度爲集合的size
    因爲,當指定數組的長度小於了集合的size,那麼該方法內部會創建一個新的數組,長度爲集合的size
     當指定數組的長度大於了集合的size,就不會創建新數組,而使用傳入的數組。
     2.爲什麼要將集合變成數組?
      爲了限定對元素的操作。不需要進行增刪了。    
  3.查找。
   二分查找指定元素:static int binarySearch(byte[] a, byte key) 使用二分搜索法來搜索指定的 byte 型數組,以獲得指定的值。 
static int binarySearch(byte[] a, int fromIndex, int toIndex, byte key)從指定區域查找。
   可以是任意類型數組:byte[].... 
 複製數組:複製整個數組:static boolean[] copyOf(boolean[] original, int newLength);original是要複製的數組,newLength是新數組的長度。
    數組類型任意:byte[]。。。
    複製數組的一部分:static boolean[] copyOfRange(boolean[] original, int from, int to) 
   深度比較: static boolean deepEquals(Object[] a1, Object[] a2) ;比較數組的內容是否相同。注意:傳入的數組必須是對象的引用,不能是基本數據類型的數組。比較基本數據類型的數組用equals方法。
   比較數組: public static boolean equals(boolean[] a,boolean[] a2):以相同順序包含相同的元素,則兩個數組是相等的.如果兩個數組都null,也是相等的。
   替換數組內容:static void fill(boolean[] a, boolean val)
    static void fill(boolean[] a, int fromIndex, int toIndex, boolean val)  
   排序:     static void sort(byte[] a) 
          static void sort(byte[] a, int fromIndex, int toIndex)不包含toIndex 
五、既操作數組又操作集合
增強for循環:既可以操作數組又可以操作集合 jdk1.5新特性
格式:for(數據類型 變量名:被遍歷的集合(Collection)或 數組)
 { 。。。 }
注意:增強for對集合進行遍歷。只能獲取集合元素。不能對集合進行操作。
 迭代器除了遍歷,還可以進行remove刪除集合中的元素的操作。如果是用ListIterator迭代器,迭代時還可進行增加修改操作。
傳統for循環和增強for循環有什麼區別?
高級for有一個侷限性。必須有被遍歷的目標。
建議在遍歷數組的時候,使用傳統for循環。因爲有角標。
 
                                     ---------------------- android培訓java培訓、期待與您交流!

                                ----------------------詳細請查看:http://edu.csdn.net/heima

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