Java集合框架由Java類庫的一系列接口威始、抽象類以及具體實(shí)現(xiàn)類組成。集合就是把一組對(duì)象組織到一起像街,然后再根據(jù)不同的需求操縱這些數(shù)據(jù)黎棠。集合類型就是容納這些對(duì)象的一個(gè)容器。根據(jù)集合中是否允許有重復(fù)的對(duì)象镰绎、對(duì)象組織在一起是否按某種順序等標(biāo)準(zhǔn)來劃分的話脓斩,集合類型又可以細(xì)分為許多種不同的子類型。
java集合框架提供了一組基本機(jī)制以及這些機(jī)制的參考實(shí)現(xiàn)畴栖,其中基本的集合接口是Collection接口随静,其他相關(guān)的接口還有Iterator接口、RandomAccess接口吗讶。
抽象類的好處:提供了接口的部分實(shí)現(xiàn)燎猛,這樣就可以在實(shí)現(xiàn)類的基礎(chǔ)上實(shí)現(xiàn)部分功能而不必重寫接口的所有方法。
Collection接口
Collection接口是集合層級(jí)結(jié)構(gòu)的根接口照皆,里面的方法有:
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c); //僅保留給定集合c中的元素(optional operation).
void clear();//清空集合
boolean equals(Object o);//繼承自O(shè)bject
int hashCode();//繼承自O(shè)bject
值得注意的是兩個(gè)toArray方法:
它們的功能都是都是返回這個(gè)集合的對(duì)象數(shù)組重绷。第二個(gè)方法接收一個(gè)arrayToFill參數(shù),當(dāng)這個(gè)參數(shù)數(shù)組足夠大時(shí)膜毁,就把集合中的元素都填入這個(gè)數(shù)組(多余空間填null)昭卓;當(dāng)arrayToFill不夠大時(shí),就會(huì)創(chuàng)建一個(gè)大小與集合相同爽茴,類型與arrayToFill相同的數(shù)組葬凳,并填入集合元素绰垂。
我們看一下Collection<E>接口的迭代器:
public interface Iterable<T> {
Iterator<T> iterator();
}
這個(gè)接口只定義了一個(gè)方法室奏,這個(gè)方法要求我們返回一個(gè)實(shí)現(xiàn)了Iterator<T>類型的對(duì)象,所以我們看下Iterator<T>的定義:
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
迭代器就是一個(gè)我們用來遍歷集合中的對(duì)象的東西劲装。即對(duì)于集合胧沫,我們不像對(duì)原始類型數(shù)組那樣通過數(shù)組索引來直接訪問相應(yīng)位置的元素,而是通過迭代器來遍歷占业。這么做的好處是將對(duì)于集合類型的遍歷行為與被遍歷的集合對(duì)象分離绒怨,這樣一來我們無需關(guān)心該集合類型的具體實(shí)現(xiàn)是怎樣的。只要獲取這個(gè)集合對(duì)象的迭代器, 便可以遍歷這個(gè)集合中的對(duì)象了谦疾。而像遍歷對(duì)象的順序這些細(xì)節(jié)南蹂,全部由它的迭代器來處理。
小總結(jié):Collection接口實(shí)現(xiàn)了Iterable<E>接口念恍,這意味著所有實(shí)現(xiàn)了Collection接口的具體集合類都是可迭代的六剥。一個(gè)迭代器對(duì)象也就是實(shí)現(xiàn)了Iterator<E>接口的對(duì)象晚顷,這個(gè)接口要求我們實(shí)現(xiàn)hasNext()、next()疗疟、remove()這三個(gè)方法该默。通常,迭代一個(gè)集合對(duì)象的代碼是這個(gè)樣子的:
Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while (iter.hasNext()) {
String element = iter.next();
//do something with element
}
以上代碼可以用增強(qiáng)for來代替:
for (String element : c) {
//do something with element
}
注意:Iterator接口的remove方法必須在next方法返回一個(gè)元素后才能調(diào)用策彤!
Collection接口是集合層級(jí)結(jié)構(gòu)的根接口栓袖。一個(gè)集合代表了一組對(duì)象,這組對(duì)象被稱為集合的元素店诗。一些集合允許重復(fù)的元素而其他不允許裹刮;一些是有序的而一些是無序的。Java類庫中并未提供任何對(duì)這個(gè)接口的直接實(shí)現(xiàn)必搞,而是提供了對(duì)于它的更具體的子接口的實(shí)現(xiàn)(比如Set接口和List接口)必指。
Collection接口的直接子接口主要有三個(gè):List接口、Set接口和Queue接口恕洲。
- List接口:
List是一個(gè)有序的集合類型(也被稱作序列)塔橡。使用List接口可以精確控制每個(gè)元素被插入的位置,并且可以通過元素在列表中的索引來訪問它霜第。列表允許重復(fù)的元素葛家,并且在允許null元素的情況下也允許多個(gè)null元素。
官方定義了以下這些方法:
ListIterator<E> listIterator();
void add(int i, E element);
E remove(int i);
E get(int i);
E set(int i, E element);
int indexOf(Object element);
上面有一個(gè)listIterator方法泌类,它返回一個(gè)列表迭代器癞谒。ListIterator<E>接口定義的方法有:
void add(E e) //在當(dāng)前位置添加一個(gè)元素
boolean hasNext() //返回ture如果還有下個(gè)元素(在正向遍歷列表時(shí)使用)
boolean hasPrevious() //反向遍歷列表時(shí)使用
E next() //返回下一個(gè)元素并將cursor(也就是指針)前移一個(gè)位置
int nextIndex() //返回下一次調(diào)用next方法將返回的元素的索引
E previous() //返回前一個(gè)元素并將cursor向前移動(dòng)一個(gè)位置
int previousIndex() //返回下一次調(diào)用previous方法將返回的元素的索引void remove() //從列表中移除最近一次調(diào)用next方法或previous方法返回的元素
void set(E e) //用e替換最近依次調(diào)用next或previous方法返回的元素
ListIterator<E>是Iterator<E>的子接口,它支持像雙向迭代這樣更加特殊化的操作刃榨。
Java類庫中常見的實(shí)現(xiàn)了List<E>接口的類有:ArrayList弹砚, LinkedList,Stack枢希,Vector桌吃,AbstractList,AbstractSequentialList等等苞轿。
- ArrayList
ArrayList是一個(gè)可動(dòng)態(tài)調(diào)整大小的數(shù)組茅诱,允許null類型的元素。Java中的數(shù)組大小在初始化時(shí)就必須確定下來搬卒,而且一旦確定就不能改變瑟俭,這會(huì)使得在很多場景下不夠靈活。ArrayList很好地幫我們解決了這個(gè)問題契邀,當(dāng)我們需要一個(gè)能根據(jù)包含元素的多少來動(dòng)態(tài)調(diào)整大小的數(shù)組時(shí)摆寄,那么ArrayList正是我們所需要的。常用方法有:
boolean add(E e) //添加一個(gè)元素到數(shù)組末尾
void add(int index, E element) //添加一個(gè)元素到指定位置
void clear()
boolean contains(Object o)
void ensureCapacity(int minCapacity) //確保ArrayList至少能容納參數(shù)指定數(shù)目的對(duì)象,若有需要會(huì)增加ArrayList實(shí)例的容量微饥。
E get(int index) //返回指定位置的元素
int indexOf(Object o)
boolean isEmpty()
Iterator<E> iterator()
ListIterator<E> listIterator()
E remove(int index)
boolean remove(Object o)
E set(int index, E element)
int size()
當(dāng)我們插入了比較多的元素锐帜,導(dǎo)致ArrayList快要裝滿時(shí),它會(huì)自動(dòng)增長容量畜号。ArrayList內(nèi)部使用一個(gè)Object數(shù)組來存儲(chǔ)元素缴阎,自動(dòng)增長容量是通過創(chuàng)建一個(gè)新的容量更大的Object數(shù)組,并將元素從原Object數(shù)組復(fù)制到新Object數(shù)組來實(shí)現(xiàn)的简软。若要想避免這種開銷蛮拔,在知道大概會(huì)容納多少數(shù)據(jù)時(shí),我們可以在構(gòu)造時(shí)指定好它的大小以盡量避免它自動(dòng)增長的發(fā)生痹升;我們也可以調(diào)用ensureCapacity方法來增加ArrayList對(duì)象的容量到我們指定的大小建炫。ArrayList有以下三個(gè)構(gòu)造器:
ArrayList()
ArrayList(Collection<? extends E> c)
ArrayList(int initialCapacity) //指定初始capacity,即內(nèi)部Object數(shù)組的初始大小
這里提一下Arraylist的值得注意的一點(diǎn)疼蛾,集合成員允許出現(xiàn)null元素肛跌,而且可以多個(gè)。請(qǐng)看一下代碼:
/**
* 關(guān)于List中是否可以添加Null的測試
* @author kyy
*/
public class Demo1 {
public static void main(String[] args) {
List<User> users=new ArrayList<User>();
for (int i = 0; i < 10; i++) {
users.add(new User());
}
//添加null不報(bào)錯(cuò)察郁,但是這樣在堆集合元素進(jìn)行方法調(diào)用時(shí)衍慎,有可能出現(xiàn)空指針異常
users.add(null);
//添加Object,報(bào)錯(cuò)
//users.add(new Object());
System.out.println(users.size());//運(yùn)行結(jié)果:11
for (User user : users) {
//這里要進(jìn)行判斷皮钠,不然有可能發(fā)生空指針異常
/*if(user!=null){
System.out.println(user.getUsername());
}*/
System.out.println(user.getUsername());
}
}
}
class User{
private String username="小明";
private int age;
public String getUsername(){
return this.username;
}
}
因?yàn)锳rraylist中允許出現(xiàn)null元素稳捆,所以在遍歷的時(shí)候,如果為對(duì)象麦轰,獲取對(duì)象屬性的時(shí)候要先判斷遍歷出來的對(duì)象是否為null乔夯,這樣才可以避免空指針異常。
而且款侵,集合不為空不代表集合長度就不為0末荐,所以在遍歷集合的時(shí)候要進(jìn)行雙重判斷,先判斷集合是否為Null,再判斷集合是否長度大于0.
- LinkedList類
LinkedList類代表了一個(gè)雙向鏈表新锈,也允許null元素甲脏。這個(gè)類同ArrayList一樣,不是線程安全的壕鹉。
這個(gè)類中主要有以下的方法:
void addFirst(E element);
void addLast(E element);
E getFirst();
E getLast();
E removeFirst();
E removeLast();
LinkedList的一個(gè)缺陷在于它不支持對(duì)元素的高效隨機(jī)訪問剃幌,要想隨機(jī)訪問其中的元素聋涨,需要逐個(gè)掃描直到遇到符合條件的元素晾浴。只有當(dāng)我們需要減少在列表中間添加或刪除元素操作的代價(jià)時(shí),可以考慮使用LinkedList牍白。jdk類庫中實(shí)現(xiàn)了List<E>接口的類有:ArrayList脊凰, LinkedList,Stack,Vector狸涌,AbstractList切省,AbstractSequentialList等等。
說到線程安全帕胆,List接口的幾個(gè)實(shí)現(xiàn)類中朝捆,Arraylist和Linkedlist都不是線程安全的,而Stack和Vector則是線程安全的懒豹,但是線程不安全的可以通過Collections的同步方法轉(zhuǎn)換為線程安全對(duì)象芙盘,具體實(shí)現(xiàn),請(qǐng)讀者翻看jdk源代碼脸秽,我相信會(huì)有另一翻收獲的儒老!
參考資料
- 《Java核心技術(shù)(卷一)》
- What is a view of a collection?
- Java SE 7 Docs