Java(集合框架)
一、集合框架(CollectionAPI)(資料處理)
集合類:面向物件對語言對事物的體現都是以物件的形式,所以為了方便對多個物件的操作,就是對物件進行儲存,集合就是儲存物件最常用的方式
集合是一系列物件的聚集(Collection)(集合就是一個儲存一組物件的容器),集合在程式設計中是一種重要的資料介面。Java中提供了有關集合的類庫稱為CollectionAPI(Collection API中的介面和類主要唯一java.util包中)
集合實際上是用一個物件代表一組物件
集合類的特點:
集合只用於儲存物件,集合的長度是可變的,集合可以儲存不同的型別的物件
陣列和集合類同是容器,有何不同?
陣列雖然也可以儲存物件,當長度是固定的,集合長度是可變的,陣列中可以儲存基本資料型別,集合只能儲存物件
從集合框架圖中可以看出,分為兩類:以Collection為介面的元素集合型別,以Map為介面的對映集合型別
二、Collection介面及其子介面
Collection介面(
Collection介面(類集介面)為List、Set和Queue介面的父介面,並且同時可以操作這三個介面,三個介面分別為:
List(列表介面):保持元素的特定的順序,且允許有重複元素
Set(資料集介面):不記錄元素的儲存順序,且不允許有重複元素(不常用)
Queue(佇列):提供的佇列實現,類似於List
Set 介面的重用實現類有HashSet(雜湊集),List介面的重要實現類有ArrayList,LinkedList和Vector(向量類,以實現類似動態陣列的功能(被ArrayList代替)),它們的關係如下圖所示。
(1) 單元素新增、刪除操作:
boolean add(Object o):將物件新增給集合
boolean remove(Object o): 如果集合中有與o相匹配的物件,則刪除物件o
(2) 查詢操作:
int size() :返回當前集合中元素的數量
boolean isEmpty() :判斷集合中是否有任何元素
boolean contains(Object o) :查詢集合中是否含有物件o
Iterator iterator() :返回一個迭代器,用來訪問集合中的各個元素
(3) 組操作 :作用於元素組或整個集合
boolean containsAll(Collection c): 查詢集合中是否含有集合c 中所有元素
boolean addAll(Collection c) : 將集合c 中所有元素新增給該集合
void clear(): 刪除集合中所有元素
void removeAll(Collection c) : 從集合中刪除集合c 中的所有元素
void retainAll(Collection c) : 從集合中刪除集合c 中不包含的元素
(4) Collection轉換為Object陣列 :
Object[] toArray() :返回一個內含集合所有元素的array
Object[] toArray(Object[] a) :返回一個內含集合所有元素的array。執行期返回的array和引數a的型別相同,需要轉換為正確型別。
此外,還可以把集合轉換成其它任何其它的物件陣列。但是,您不能直接把集合轉換成基本資料型別的陣列,因為集合必須持有物件。Collection不提供get()方法。如果要遍歷Collectin中的元素,就必須用Iterator
Collection介面中重要的方法有:
public boolean add(Object o); //新增元素
public boolean remove(Object o); //移除元素
public void clear(); //清除所有元素
public boolean contains(Object o); //判斷是否包含某元素
public int size(); //元素個數
public boolean isEmpty(); //判斷是否為空
public Iterator iterator(); //得到迭代器
1.List介面(容器)
List 介面繼承了 Collection 介面以定義一個允許重複項的有序集合(有序且可重複)
在“集合框架”中有兩種常規的 List 介面實現類:ArrayList 和 LinkedList(連結串列)(ArrayList 和 LinkedList 都實現 Cloneable 介面,都提供了兩個建構函式,一個無參的,一個接受另一個Collection),使用兩種 List 中的哪一種取決於特定的需要( LinkedList內部封裝的是雙向連結串列的資料結構, 不同的資料有不同的資料結構,不同的資料結果操作起來效能不一樣,連結串列資料結構,做插入、刪除的效率高(可基於 LinkedList實現棧和佇列),但查詢效率低,而陣列結構(ArrayList),做查詢時效率高,但插入和刪除效率低(做移位操作))
class MyStack<T>{//實現棧的功能(後進先出),採用LinkedList的資料結構
private LinkedList<T> data=null;
public MyStack(){
data=new LinkedList<T>();
}
//壓棧的方法
public void push(T obj){
data.addFirst(obj);
}
public T pop(){
return data.removeFirst();
}
public Iterator<T> iterator(){//必須在該類中定義該方法
return data. iterator();
}
}
2.Set介面(容器)
Set 介面繼承 Collection 介面,基本與Collection方法相同,只是行為不同(Set不允許包含重複元素,並且最多包含一個null元素)
Set集合不允許重複元素,是因為Set判斷兩個物件相同不是使用==運算子,而是根據equals方法。即兩個物件用equals方法比較返回true(不能新增到Set集合中)
1. HashSet實現類(底層是雜湊表,不能確保元素順序)
HashSet的特點:
(1)HashSet不是同步的,多個執行緒訪問是需要通過程式碼保證同步
(2)集合元素值可以使null
HashSet集合判斷兩個元素相等的標準是兩個物件通過equals方法比較相等,並且兩個物件的hashCode()方法返回值也相等
LinkedHashSet(HashSet的子類)集合也根據元素hashCode值來決定元素儲存位置,但也同時使用連結串列維護元素的次序,即當遍歷LinkedHashSet集合元素時,HashSet將會按元素的新增順序來訪問集合裡的元素
2. TreeSet實現類 TreeSet是SortedSet介面的唯一實現,TreeSet可以確保集合元素處於排序狀態(元素是有序的)
TreeSet的內部操作的底層資料時TreeMap(只是我們操作的是TreeMap的key)
}
public class TreeSetDemo1 {
public static void main(String[] args) {
TreeSet<Person> pset = new TreeSet<Person>();
pset.add(new Person("zhangsan", 28));
pset.add(new Person("lisi", 18));
pset.add(new Person("wangwu", 25));
pset.add(new Person("zhaoliu", 14));
//System.out.println(pset);
// 打印出來
Iterator<Person> it = pset.iterator();//通過迭代器進行迭代
while (it.hasNext()) {//判斷是否有資料
Person p = it.next();//取出的是Person型別
System.out.println(p.getName()+"--"+p.getAge());
}
}
三、兩種遍歷集合的方法(Iterator介面和foreach迴圈)
1. Iterator介面
Iterator介面也是Java集合框架的成員,主要用於遍歷(即迭代訪問)Collection集合中的元素,並安全的從Collection 中除去適當的元素,也稱為迭代器(Collection 介面的iterator()方法返回一個 Iterator)
Iterator中刪除操作對底層Collection也有影響,迭代器是故障快速修復(fail-fast)的。這意味著,當另一個執行緒修改底層集合的時候,如果您正在用 Iterator 遍歷集合,那麼,Iterator就會丟擲 ConcurrentModificationException (另一種 RuntimeException異常)異常並立刻失敗
(1) boolean hasNext():
判斷集合裡是否存在另一個可訪問的元素
(2) Object next():
返回集合裡下一個元素(如果到達集合結尾則丟擲NoSuchElementException異常)
(3) void remove():
刪除集合裡上次訪問返回的元素(本方法必須緊跟在一個元素的訪問後執行,如果上次訪問後合已被修改,方法將丟擲IllegalStateException)
2、使用foreach迴圈遍歷集合元素
(1)格式:
for(元素型別 t 元素變數 x : 遍歷物件A) {
// 程式塊
}
(2)說明:a. foreach簡化了對陣列和集合的遍歷,如果不希望遍歷整個集合,或者在迴圈內部需要操作下標值就需要使用傳統的for迴圈
b. 簡化了程式設計,提高了程式碼的可讀性和安全性(不用怕陣列越界)
c. foreach一般結合泛型使用
四、兩種比較介面(Comparable介面和Comparator介面)
在集合框架中有兩種比較介面:Comparable介面和Comparator介面,像String和Integer等Java內建類實現Comparable介面以提供一定排序方式,但這樣只能實現該介面一次。對於那些沒有實現Comparable介面的類、或者自定義的類,可以通過Comparator介面來定義您自己的比較方式
1. Comparable介面
在java.lang包中,Comparable介面適用於一個類有自然順序的時候。假定物件集合是同一型別,該介面允許您把集合排序成自然順序。
int compareTo(Object o):比較當前例項物件與物件o,如果位於物件o之前,返回負值,如果兩個物件在排序中位置相同,則返回0,如果位於物件o後面,則返回正值
2. Comparator介面
若一個類不能用於實現java.lang.Comparable,或者不喜歡預設的Comparable行為並想提供自己的排序順序(可能多種排序方式),可以去實現Comparator介面,從而定義一個比較器。
(1) int compare(Object o1, Object o2):對兩個物件o1和o2進行比較,如果o1位於o2的前面,則返回負值,如果在排序順序中認為o1和o2是相同的,返回0,如果o1位於o2的後面,則返回正值
(2) boolean equals(Object obj):指示物件obj是否和比較器相等
五、Map介面及其子介面
Map用於儲存具有對映關係的資料(key-vlaue),是一個儲存鍵/值對的物件(鍵必須是唯一的,值是可以重複的)(存放具有鍵值對格式資料的容器)
Map的key是以後用於檢索值得物件,給定一個鍵和一個值,可以儲存這個值到一個Map物件中(可以使用對應鍵進行檢索值);Map的value非常類似List:元素與元素之間可以重複,每個元素可以根據索引(key)來查詢(Map有時也稱為字典,或關聯陣列)
支援對映的介面有:Map(對映唯一關鍵字給值)、Map.Entry(內部類(介面),描述對映中的元素)和SortedMap(擴充套件Map以便關鍵字按升序保持)
Map中包括一個Entry內部類(封裝了一個鍵值對(key-value對))
包含三個方法:Object getKey():返回該Entry裡包含的key值
Object getValue():返回該Entry裡包含的value值
Object setValue():設定並返回該Entry裡包含的新設定的value值
Map介面中定義如下方法:
Set entrySet():返回Map中所有包含的key-value對組成的Set集合,每個集合元素都是Map.Entry物件(把Map理解成一個特殊的Set,只是該Set裡包含的集合元素是Entry物件,而不是普通物件)
Set keySet():返回該Map中所有key所組成的set集合
1. HashMap和Hashtable實現類
HashMap和Hashtable都是Map介面的實現類,Hashtable是一個古老的Map實現(JDK1.0),Hashtable是一個執行緒安全的Map實現,但HashMap(JDK1.2)是執行緒不安全的實現,但HashMap比Hashtable的效能高些
HashMap可以使用null作為key或value( 由於HashMap裡的可以不能重複,最多隻有一對key-value值為null,但可以有無數多項key-value對的value為null) HashMap重寫了toString()方法方法總是返回如下格式的字串:{key1 = value1,key2 = value2..}
HashMap底層其實是個16長度的陣列(hashMap內部的結構是陣列連結串列結構(單向連結串列)
不同的key有可能算出來是相同的雜湊值,根據雜湊值計算出存放到陣列的下標(有衝突)
key一樣(同一個物件,覆蓋)
雜湊值一樣(key不一樣),移到後面(已建立一個Entry物件)//雜湊函式能減小衝突
實現過程:
//hashmap會呼叫預設構造方法會產生一個長度為16的Entry陣列。
//int hash=hash(key.hashCode());
// 首先呼叫key的hashCode方法得來的一個整數(雜湊碼)
//把雜湊碼作為引數傳遞到hash(雜湊函式)中來進行運算(雜湊運算),得到一個整數(雜湊值)
// int i=indexFor(hash,table.length);
//把雜湊值和陣列的長度來進行計算,最終得到存放到陣列的位置(下標)
//通過Set容器輸出HashMap中所有鍵值對(一般用該方法)
//當我們呼叫put(key,value)這個方法的時候,首先會把key和value封裝Entry靜態內部類中
//把Entry再新增到陣列中,所以我們只要獲取陣列中的所有Entry物件(封裝鍵值對),接下來呼叫Entry物件中的getKey()和getValue()方法就能獲得鍵值對
Set<Entry<String,String>> entrys=map.entrySet();//entrySet():返回此對映中所包含的鍵的 set 檢視
for(Entry<String,String> entry:entrys){
System.out.println(entry.getKey()+"--"+entry.getValue());
}
2. LinkedHashMap類(HashMap的子類)
LinkedHashMap是雙向連結串列來維護key-value對的次序(內部順序),該連結串列定義了迭代順序,該迭代順序與key-value對的插入順序保持一致
LinkedHashMap可以避免對HashMap、Hashtable裡的key-value對進行排序(只要插入key-value對時保持順序即可),LinkedHashMap需要維護元素的插入順序(效能略低於HashMap的效能,但在迭代訪問Map裡的全部元素時將有很好的效能)
3. SortedMap介面和TreeMap實現類
Map介面派生了一個SortedMap子介面,TreeMap為其實現類(底層資料結構為紅黑樹)。類似TreeSet排序,TreeMap也是基於紅黑樹對TreeMap中所有key進行排序(保證TreeMap中所有key-value對處於有序狀態(自動排序))
(1)TreeMap兩種排序方法:
自然排序:TreeMap的所有key必須實現Comparable介面,而且所有key應該是同一個類的物件,否則將會丟擲ClassCastExcepiton異常
定製排序:建立TreeMap時,傳入一個Comparator(比較器)物件,該物件負責對TreeMap中所有key進行排序(不要求Map的key實現Comparable介面)
(2) TreeMap中判斷兩個key相等的標準也是兩個key通過equals比較返回true,而通過compareTo方法返回0, 如果使用自定義的類作為TreeMap的key,應重新該類的equals方法和compareTo方法時應有一致的返回結果:即兩個key通過equals方法比較返回true時,通過compareTo方法比較應該返回0
總結:Map集合與Set集合元素的儲存形式很像,如Set介面下有HashSet、LinkedHashSet、SortedSet(介面)、TreeSet、EnumSet等實現類和子介面,而Map介面下則有HashMap、LinkedHashMap、SortedMap(介面)、TreeMap、EnumMap等實現類和子介面。
六、Collections類的使用
Collections類 (類集工具類)定義了若干用於類集和對映的演算法(靜態方法),可對集合中的元素進行相關操作,為Collection和Map介面的實現類提供服務(交換(swap)、自然排序(sort)、二分化查詢(binarySearch)、打亂順序(shuffle)、填充替換(fill))
七、java集合總結
如下是集合介面的UML圖,第一部分展現的是介面部分(表示不同集合型別),第二部分展現的是抽象類(對集合介面的部分實現,可擴充套件為自定義集合類),第三個部分展現的是實現類(對介面的具體是實現)
在很大程度上,一旦您理解了介面,您就理解了框架。雖然您總要建立介面特定的實現,但訪問實際集合的方法應該限制在介面方法的使用上;因此,允許您更改基本的資料結構而不必改變其它程式碼
java集合框架裡面所有具體類都實現了cloneable和Seriallizable介面,所以它們都可以實現克隆和可序列化
(一)將介面的引用指向實現類的物件
java.util中的類ArrayList實現了介面List則生成ArrayList物件時可用:List list=new ArrayList(); 也就是說所有實現了介面List的類,都可以用List介面來宣告物件型別,然後用實體類進行例項化(表明了介面可以用來作為型別的表述.當然,生成的物件list只能呼叫介面List中提供的方法)這樣的用法可以大大提高程式設計的靈活性。
ArrayList<String> nList = new ArrayList<String>(); // 採用介面List引用物件,將介面的引用指向實現類的物件
nList.add("chenghao");// 通過泛型限定了容器中物件的型別
List<String> nList = new ArrayList<String>();//兩個字:多型。好處:便於程式的二次開發與維護
(二)使用迭代器來進行統一的遍歷
1.Iterable介面
(1) 使用該介面允許物件成為foreach語句的目標,即該集合物件允許迭代
(2) 類集介面Collection是Iterable的子介面。所以所有類集物件可以迭代訪問,而對映Map不行
(3) Iterator<T> Iterator(返回一組T型別的元素上進行迭代的迭代器)
迭代器是實現了Iterator和ListIterator介面的類的物件,可以通過遍歷類集,訪問操作其中的每個元素
System.out.println("使用迭代器來進行統一的遍歷:");
Iterator<String> it = nList.iterator();// 返回迭代器介面的一個實現類物件
while (it.hasNext()) {
String name = it.next();// 取出,返回物件
System.out.println(name);
}
System.out.println("使用增強的for迴圈遍歷");
for (String name : nList) {
System.out.println(name);
}
}
Collection容器
1、首先我們要了解為什麼JAVA中要有容器?
我們知道陣列的長度一旦指定,程式在執行過程中就不能改變,正是因為陣列有這樣的缺陷,所以才出現就(collection)容器,容器的大小可以任意改變,容器的大小就是根據你實際放入了多少的資料而相應的變大。
2、容器中包括Set 和List,那麼他們有什麼區別呢?
Set是無序唯一的,底層是用Map實現的;List是有序不唯一的,底層是陣列,所以可以用索引來得到值
注意:在list中刪除資料時,當刪除的是裡面的整數資料型別時要知道使用包裝型別進行刪除,我們要知道JAVA會自動裝箱和拆箱 但是在Set就不需要使用包裝型別來刪除,因為Set中沒有索引
3、List又分為Arrylist和LinkedList,他們之間又有什麼區別呢?各適用於什麼情況呢?
ArrayList(陣列)的記憶體地址連續,這個容器中的資料是經常用來查詢的;LinkedList(連結串列)記憶體地址不連續,這個容器中的資料經常用來更新
4、Iterator(容器的迭代器)
所有實現了Collection介面的類都有一個iterator方法,這個方法會得到一個實現了Iterator的類的物件;List雖然有索引,但是也可以使用JAVA5.0以後出來的增強for迴圈,其實就相當於.Net中的foreach(),但是因為不能方便的操作索引,只用於簡單的遍歷,如果增強的for迴圈迭代的是容器的話內部預設的也是使用的Iterator迭代器實現的。但是也必須要使用迭代器來實現,因為這是唯一縣城安全的方法
5、Collections類
該類提供了一系列的方法來對List進行操作,比如說我們自己寫的類要進行比較大小或者是排序,那麼和它相關的一切規則需要我們自己來定義,比如比較物件是否相等或者是判斷大小的規則,如果是要實現比較大小的類一定要實現Comparable介面然後重寫public int compareTo(Object o)方法,為了更美觀的輸出到控制檯上也建議重寫public String toString()方法。
6、JAVA5.0之後出現的泛型
概念:指定當前的容器中只能放置什麼樣的型別,並且泛型中沒有父類的引用指向子類的物件
7.容器中為什麼會出現Map?
因為Set是無序的,唯一的,hashSet沒有索引,不方便通過索引來對容器進行操作,所以出現了Map,所以Map就是可以建立索引,而且這個索引還是自己定義的。Map是以Key-value(鍵-值)對的形式來儲存資料的,新增資料是使用Map.put( index,value);index是自定義的型別,可以是自己規定的;獲取資料是使用Map.get(index) 其實Map就相當於List只是Map是添加了索引的功能
8.
集合的遍歷
Java8中增加了lambda表示式來遍歷集合,我們這裡採用的是傳統的Iterator和foreach迴圈來遍歷,在遍歷過程中,是不能修改集合元素的,只能通過迭代器本身的方法去修改,否則會引發
ConcurrentModificationException異常,這樣可以避免共享資源引發的潛在問題