Java進階—— 集合體系詳解之List體系有序集合
引言
面嚮物件語言對事物的體現必然是以物件的形式,Java工程師為了方便多多個物件的操作,就對物件進行儲存,集合就是儲存物件的一種方式,他們的底層都是基於不同的資料結構。當然集合和陣列一樣都是容器,陣列也是可以儲存物件的,但是陣列長度一經初始化長度就是固定的,而集合長度是可變的,陣列只能用於儲存相同型別的物件,而集合可以儲存不同型別的物件,資料多了用物件封裝,物件多了用集合存。
一Java 集合類體系結構
Java的所有的集合體系都是實現了Collection介面,所有通用的 Collection 實現類(通常通過它的一個子介面間接實現 Collection)應該提供兩個“標準”構造方法:一個是 void(無引數)構造方法,用於建立空 collection;另一個是帶有 Collection 型別單引數的構造方法,用於建立一個具有與其引數相同元素新的 collection。add方法的引數型別是Object,以便於利用多型特性接收任意型別的物件,集合中儲存的都是物件的引用(地址)
List、Set、Map體系。
二集合的迭代器
1 迭代器的實質及設計思想
迭代器其實質只是一個介面,只是取出集合中元素的一種方式,為了專業些給了他一個叫名字曰:迭代器。再深究其思想:每一個容器都有存和取的方式,而且每個容器的資料結構也許都不一樣,所以存取的動作實現的方式也有可能不一樣,為了用同一個描述不同集合讀取方式而把這個一操作封裝為一個類,而這個類是在各個集合類的內部(因為要操作集合內部的資料成員,內部類最方便)且這個內部類都繼承了Iterator介面,Iterator主要是封裝了判斷和取出資料操作。每一個容器都對外暴露了一個方法即Iterator(),只要呼叫就能得到對應的迭代器。不知道你們是否受到了啟發?反正我是學習到了他的設計思想。(把資料封裝為類,抽取共性作為父類或者介面,再結合多型
2 迭代器的應用
再回到頂層介面Collection,接口裡定義了一個Iterator iterator() <返回在此 collection 的元素上進行迭代的迭代器>,既然所有的集合類都是實現了Collection介面所以我們只要呼叫對應集合類中複寫了的 Iterator()就得到了迭代器,都能夠藉助迭代器裡的定義的方法hasNext()、next()、remove()去遍歷集合元素。
3 List體系特有的迭代器ListIterator
List體系下由於Iterator在迭代時不能通過集合物件的方法操作集合中的元素,因為會發生併發異常,所以在迭代時只能用迭代器的方法操作元素,可Iterator方法是有限的,只能對元素進行判斷、取出、刪除的操作,如果要進行其他操作如新增、修改等就應該使用特有的ListIterator。
三 List集合體系
List集合體系即實現了List介面的所有集合類,最常用的是ArrayList、LinkedList這兩個集合類。
1 List集合的特點
- 元素是有序的,元素可以重複,因為該集合體繫有索引
- List體系下由於Iterator在迭代時不能通過集合物件的方法操作集合中的元素,因為會發生異常。
2 ArrayList儲存物件的過程及應用
2.1)集合中儲存的是物件的地址
舉個例子說明,我們要把一個Student物件儲存到ArrayList集合裡,在棧中會儲存s、al變數(基本型別的變數和物件的引用變數都是在函式的棧記憶體中分配),由於new了兩個物件而在堆記憶體中會分配到兩個地址空間(堆記憶體用於存放由new建立的物件和陣列),一個用於儲存new Student(),一個用於儲存new ArrayList(),當add之後,才會把new Student()的地址儲存到集合中。
Student s=new Student();
ArrayList<Student> al=new ArrayList<Student> ();
al.add(s);
2.2)ArrayList集合的應用
- 例1 :儲存資料到ArrayList中並遍歷
//建立ArrayList物件
ArrayList<String> al=new ArrayList<String>();
al.add("初");
al.add("識");
al.add("Java");
al.add("集合");
al.add("框架");
//遍歷
Iterator<String> it=al.Iterator();//介面只能指向自己的子類物件,獲取迭代器用於出去集合中的元素
while(it.hasNext()){
System.out.println(it.next());//取出每一個元素
}
若在迭代過程中,使用集合物件準備新增或者刪除元素時會產生併發修改異常,把遍歷程式碼改成這樣:
while(it.hasNext()){
if(obj.equals("Java")){
al.add("任性新增"); //執行時報異常
}
System.out.println(it.next());//取出每一個元素
}
但是不是使用集合物件去操作,而是該用迭代器物件去操作集合的資料,則可以正常執行,比如刪除掉指定元素,把程式碼改為:
System.out.println(al);//輸出:["初","識","Java","集合","框架"]
Iterator<String> it=al.Iterator();//介面只能指向自己的子類物件,獲取迭代器用於出去集合中的元素
while(it.hasNext()){
it.next();//取出每一個元素
//若在迭代過程中,使用集合物件進行準備新增或者刪除元素時會產生併發修改異常,應該同樣適用迭代器物件來進行,比如 it.remove();
Object obj=new Object();
if(obj.equals("Java")){
it.remove();//會正常執行,並把元素Java引用從集合移除掉,但是Java還是存在於記憶體中,因為Java的引用還在被obj引用所以被打印出來
}
//但是迭代過程還是會先輸出被刪除的元素:"初","識","Java","集合","框架"
}
System.out.println(al);//輸出:["初","識","集合","框架"]
再一次證明的集合儲存的是物件的地址。
2. 例2:去除ArrayList中的重複元素
public ArrayList removeRepeatElem(ArraList al){
//定義個臨時容器
ArrayList tmp=new ArrayList();
Iterator it=new al.iterator();
while(it.hasNext()){
Object obj=it.next();
if(!tmp.contains(obj)){
tmp.add(obj);
}
}
return tmp;
}
2 LinkedList連結串列的應用
LinkedList的使用和ArrayList沒多大區別,兩者相比LinkedList的優勢在於更加便於進行頻繁的修改操作。下面用LinkedList來模擬Queue佇列資料結構
Class MyQueue{
private LinkedList link;
Quee(){
link=new LinkedList();
}
private void myAdd(Object obj){
link.addFirst(obj);
}
private Object myGet(){
link.removeLast();
}
private boolean isNull(){
return link.isEmpty();
}
。。。
}
3 向量Vector(儘量不要使用)
向量Vector其實就是可變長度的陣列,ArrayList完全可以替代,向量中的列舉。有點類似迭代器,應優先考慮迭代器。因為列舉的名稱及方法的名稱都過長所以被迭代器取代了。
Vector e=new Vector();
e.add("初");
e.add("識");
e.add("Java");
e.add("集合");
e.add("框架");
Enumeration en =v.elements();//獲取列舉,可以理解為迭代器該怎麼用還是怎麼用