什么是集合(Collection)?集合就是“由若干個(gè)確定的元素所構(gòu)成的整體”彩郊。例如前弯,5只小兔構(gòu)成的集合:
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
│ (_(\ (_/) (_/) (_/) ((\ │
( -.-) (?.?) (>.<) (.) (='.')
│ C(")(") (")(") (")(") (")(") O(_")") │
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
一組類似的數(shù)據(jù)
在Java中,如果一個(gè)Java對(duì)象可以在內(nèi)部持有若干其他Java對(duì)象秫逝,并對(duì)外提供訪問(wèn)接口恕出,我們把這種Java對(duì)象稱為集合。很顯然违帆,Java的數(shù)組可以看作是一種集合:
String[] ss = new String[10]; // 可以持有10個(gè)String對(duì)象
ss[0] = "Hello"; // 可以放入String對(duì)象
String first = ss[0]; // 可以獲取String對(duì)象
既然Java提供了數(shù)組這種數(shù)據(jù)類型浙巫,可以充當(dāng)集合,那么刷后,我們?yōu)槭裁催€需要其他集合類的畴?這是因?yàn)閿?shù)組有如下限制:
- 數(shù)組初始化后大小不可變;
- 數(shù)組只能按索引順序存取尝胆。
因此丧裁,我們需要各種不同類型的集合類來(lái)處理不同的數(shù)據(jù),例如:
- 可變大小的順序鏈表含衔;
- 保證無(wú)重復(fù)元素的集合煎娇;
Collection
Java標(biāo)準(zhǔn)庫(kù)自帶的java.util
包提供了集合類:Collection
二庵,它是除Map外
所有其他集合類的根接口。Java的java.util包主要提供了以下三種類型的集合:
List
:一種有序列表的集合缓呛,例如催享,按索引排列的Student的List;
Set
:一種保證沒(méi)有重復(fù)元素的集合哟绊,例如因妙,所有無(wú)重復(fù)名稱的Student的Set;
Map
:一種通過(guò)鍵值(key-value)查找的映射表集合匿情,例如兰迫,根據(jù)Student的name查找對(duì)應(yīng)Student的Map。
Java集合的設(shè)計(jì)有幾個(gè)特點(diǎn):一是實(shí)現(xiàn)了接口和實(shí)現(xiàn)類相分離
炬称,例如汁果,有序表的接口是List,具體的實(shí)現(xiàn)類有ArrayList玲躯,LinkedList等据德,二是支持泛型
,我們可以限制在一個(gè)集合中只能放入同一種數(shù)據(jù)類型的元素跷车,例如:
List<String> list = new ArrayList<>(); // 只能放入String類型
最后棘利,Java訪問(wèn)集合總是通過(guò)統(tǒng)一的方式——迭代器(Iterator
)來(lái)實(shí)現(xiàn),它最明顯的好處在于無(wú)需知道集合內(nèi)部元素是按什么方式存儲(chǔ)的朽缴。
Java的集合類定義在java.util包中善玫,支持泛型,主要提供了3種集合類密强,包括List茅郎,Set和Map。Java集合使用統(tǒng)一的Iterator遍歷或渤,盡量不要使用遺留接口系冗。
List
在集合類中,List是最基礎(chǔ)的一種集合:它是一種有序列表薪鹦。
ArrayList把添加和刪除的操作封裝起來(lái)掌敬,讓我們操作List類似于操作數(shù)組,卻不用關(guān)心內(nèi)部元素如何移動(dòng)池磁。
我們考察List<E>接口奔害,可以看到幾個(gè)主要的接口方法:
在末尾添加一個(gè)元素:boolean add(E e)
在指定索引添加一個(gè)元素:boolean add(int index, E e)
刪除指定索引的元素:int remove(int index)
刪除某個(gè)元素:int remove(Object e)
獲取指定索引的元素:E get(int index)
獲取鏈表大小(包含元素的個(gè)數(shù)):int size()
但是地熄,實(shí)現(xiàn)List接口并非只能通過(guò)數(shù)組(即ArrayList的實(shí)現(xiàn)方式)來(lái)實(shí)現(xiàn)舀武,另一種LinkedList通過(guò)“鏈表”也實(shí)現(xiàn)了List接口。在LinkedList中离斩,它的內(nèi)部每個(gè)元素都指向下一個(gè)元素:
┌───┬───┐ ┌───┬───┐ ┌───┬───┐ ┌───┬───┐
HEAD ──>│ A │ ●─┼──> │ B │ ●─┼──> │ C │ ●─┼──> │ D │ │
└───┴───┘ └───┴───┘ └───┴───┘ └───┴───┘
我們來(lái)比較一下ArrayList和LinkedList:
ArrayList LinkedList
獲取指定元素 速度很快 需要從頭開始查找元素
添加元素到末尾 速度很快 速度很快
在指定位置添加/刪除 需要移動(dòng)元素 不需要移動(dòng)元素
內(nèi)存占用 少 較大
通常情況下银舱,我們總是優(yōu)先使用ArrayList瘪匿。
ArrayList類中
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
public Iterator<E> iterator() {
return new ArrayList.Itr();
}
}
private class Itr implements Iterator<E> {
int cursor;
int lastRet = -1;
int expectedModCount;
Itr() {
this.expectedModCount = ArrayList.this.modCount;
}
public boolean hasNext() {
return this.cursor != ArrayList.this.size;
}
public E next() {... 具體實(shí)現(xiàn)}
}
所以我們要始終堅(jiān)持使用迭代器Iterator來(lái)訪問(wèn)List。Iterator本身也是一個(gè)接口寻馏,但它是由List的實(shí)例調(diào)用iterator()方法的時(shí)候創(chuàng)建的棋弥。Iterator對(duì)象知道如何遍歷一個(gè)List,并且不同的List類型诚欠,返回的Iterator對(duì)象實(shí)現(xiàn)也是不同的顽染,但總是具有最高的訪問(wèn)效率。
實(shí)際上轰绵,只要實(shí)現(xiàn)了Iterable接口的集合類都可以直接用for each循環(huán)來(lái)遍歷粉寞,Java編譯器本身并不知道如何遍歷集合對(duì)象,但它會(huì)自動(dòng)把for each循環(huán)變成Iterator的調(diào)用左腔,原因就在于Iterable
接口定義了一個(gè)Iterator<E> iterator()
方法唧垦,強(qiáng)迫集合類必須返回一個(gè)Iterator實(shí)例。
有序列表集合液样,有索引振亮,類似數(shù)組
數(shù)組添加刪除操作不方便
ArrayList把添加刪除操作都封裝起來(lái),讓我們操作List類似于操作數(shù)組鞭莽,卻不關(guān)心內(nèi)容元素如何移動(dòng)