容器也是Java面試經常問到的問題. 也是Java編程的其中1個難點,在一篇文章中很難全部講清楚, 我打算分幾篇逐步介紹。
Java裡的容器的定義很簡單:
容器(Collection)也稱為集合, 在java中就是指對象的集合.
這裡可以看出1個特性: 容器裡存放的都只能是對象. 實際上是存放對象的指針(頭部地址).
在編程中我們通常需要將若干個對象進行集中處理.
當然. 通過數組也可以實現此需求.
但是數組也存在兩個硬傷:
1. 數組中元素的元素必須相同.
2. 數組的長度難以擴充
這裡解析一下:
假如A是1個類名, 那麼
A arr_A[] = new A[10];
標識在內存裡分配了1個數組, 數組的長度是10.
首先這個數組的元素只能是A的對象, 這個肯定的了.
假如想擴充這個數組, 那麼只能重新建立1個長度更長的數組, 並使用System.arraycopy() 函數把原數組的內容copy到新數組裡.
這樣的話十分耗費cpu資源和內存空間.
例子:
package Collection_kng;
class A{
private int i;
public A(int i){
this.i = i;
}
public String toString(){
return "" + this.i;
}
}
public class Collect1{
public static void f(){
A[] arr_A = new A[10];
int i;
for(i = 0; i < 10; i++){
arr_A[i] = new A(i);
}
A[] arr_A2 = new A[11];
System.arraycopy(arr_A,0,arr_A2,0,10);
arr_A2[10] = new A(11);
for(i = 0; i < 11; i++){
System.out.println(arr_A2[i]);
}
}
}
所以講: 對於1個數組, 一旦分配內存, 如果你想改變內存長度, 則效率會很低.
而使用容器的話, 可以解決這個兩個問題.
也就是講: 一個容器可以存放不同類型的對象(實際上是對象的指針), 而且可以靈活擴充容器的長度.
例子:
public static void g(){
ArrayList arr_l = new ArrayList();
int i;
for(i = 0; i < 10; i++){
arr_l.add(new A(i));
}
arr_l.add(new A(11));
for(i = 0; i < 11; i++){
System.out.println(arr_l.get(i));
}
}
在數學上, 我們可以把容器分為三種.
1.集(Set)
Set相當於數學上的集合, Set中的所有元素是無序的, 而且不允許出現重復的元素.
2.列表(List)
List相當於代數裡的隊列, 列表的元素是有序的, 而且允許包含重復成員.
當然, 在java中List也可以分為數組列表(ArrayList)和鏈表(LinkedList), 它們兩者的使用方法類似, 但是內存存儲機制是不同的.
3.映射(Map)
Map保存所謂的'鍵值對'(Key - Value)信息, 映射中不能出現重復的鍵(key), 每個鍵最多只能映射一個值.
在java中, sun公司為上面三個容器類型設計了三個對應的接口(interface), 這個三個接口分別是 Set List Map.
框架圖如下:
可以見到, 對於Set來講, Set接口繼承了Collection接口, HashSet這個容器(類)實現了Set接口.
而ArrayList和 LinkedList都實現了List接口, List接口也是繼承自Collection接口.
而HashMap容器則是實現了Map接口, 而Map接口並不繼承與Collection接口哦.
由此看出, Java裡的容器類是基於接口(interface)構建的.
* Collection 定義了存取一組對象的方法, 其子接口Set和List分別定義了存儲方式.
* Map接口定義了存儲(key-value)映射對的方法.
很多時候, 大家還會見到另1中容器Vector, 其實Vector也是List容器一種, 但是Vector支持線程同步(裡面的方法都是sycronized的), 也就是允許多個線程同時操作1個Vector容器而不丟失數據.
而Arraylist並不是同步的.
容器作為1個類, 當然有若干個常用的成員方法, 為了敘述方便, 先介紹這一點.
返回Collection中的元素個數.
判斷1個容器是否包含另1個容器的所有元素, 這裡的元素指的的是對象的指針, 也就是對象的本身.
把1個對象添加到容器中, 這個方法是每種容器必有的方法.
移除容器中首次出現的制定對象, 因為List容器根據次序不同允許存在重復的對象
獲得容器中第index個元素, 這個方法在List容器中十分常用, 但是不並適用於Set容器, 因為Set容器的元素是無序的.
一些無序的容器很難通過循環來遍歷元素, 這時我們可以利用迭代器來遍歷容器元素.
容器不是數組, 不能用下標來訪問容器的元素, 這個方法可以返回1個包含容器所有元素的數組.
很多時候, 我們需要循環輸出容器元素的對象.
例如:
for(...){
System.out.println(arr1.get(i));
}
注意利用get()方法返回的是1個對象.
而println方法是輸出對象的toString()方法. toString()方法在基類(Object)中被定義成輸出對象的類名+hashcode().
而很多時候我們需要的是輸出對象的其他有用信息(例如關鍵成員的值)
所以強烈建議:
為所有有可能放入容器的類重寫toString()方法.
上面提到了, Collection其實是1個接口, 但是Java裡也存在1個類叫Collections, 而這個Collections也是跟容器有關的.
我們常用的容器, 例如Arraylist, HashSet, Linkedlist等都是基於Collection接口實現的.
所以 Collections並不是上述容器的超類.
實際上, 在java裡的容器(例如Arraylist, Linklist)類只提供了一些簡單的操作方法, 如add(), get(), size()等.
而一些復雜的功能, 例如排序(sort), 倒置(reverst),則沒用提供.
所以Java提供了另1個類Collections, 集合了很多隊容器進行復雜操作的靜態方法. 這個就是Collections類的意義.
下面列舉若干Collections類的常用方法
對List容器內的元素排序, 前提是容器內元素的類已經實現Comparable 接口(可以比較的)
下一篇文章會詳細提到.
對List容器的元素進行隨機排序.
就是傳說中的倒置, 隊List容器內的元素進行逆排序, 一般沒什麼用..
把容器內的所有元素都替換為1個制定對象(object)
把容器src裡的內容復制到dest容器
對於已排序的容器, 利用折半查找法找出制定對象.