Java集合(一):Java集合概述
注:本文基于JDK 1.7
1 概述
Java提供了一個(gè)豐富的集合框架,這個(gè)集合框架包含了許多接口、虛擬類和實(shí)現(xiàn)類。這些接口和類提供了豐富的功能,能夠滿足基本的聚合需求。下圖就是這個(gè)框架的整體結(jié)構(gòu)圖:
可以看見,這個(gè)框架非常大,大到吃驚的地步。這個(gè)圖的左面部分是接口,右面部分是類,中間的線代表了右面的類實(shí)現(xiàn)了左面的哪些接口。比如,AbstractList類實(shí)現(xiàn)了List接口,那么繼承自AbstractList類的子類都實(shí)現(xiàn)了這個(gè)接口。還有,如果一個(gè)類實(shí)現(xiàn)了一個(gè)接口,那么這個(gè)類也實(shí)現(xiàn)了這個(gè)接口的所有父接口。比如,TreeSet類實(shí)現(xiàn)了Deque接口,那么TreeSet類也實(shí)現(xiàn)了Queue接口、Collection接口和Iterable接口(接口Collection擴(kuò)展了Iterable,這里沒顯示出來)。
圖中紅色的類表示抽象類,大部分抽象類都以Abstract開頭,除了Dictionary。綠顏色的類表示遺留類,這些類在Java一開始的時(shí)候就已經(jīng)存在了。
2 接口與實(shí)現(xiàn)分離
Java集合類庫中將接口(interface)與實(shí)現(xiàn)(implementation)分離。這里以隊(duì)列(queue)為例。
隊(duì)列接口指出可以在隊(duì)列的尾部添加元素,在隊(duì)列的頭部刪除元素,并且可以查找隊(duì)列中的元素個(gè)數(shù)。即隊(duì)列中的元素按照先進(jìn)先出的元素使用隊(duì)列。
如果我們?cè)O(shè)計(jì)queue的接口,可能這樣設(shè)計(jì):
interface Queue<E> {void add(E element);E remove();int size(); }這里給出了三個(gè)基本的方法。這個(gè)接口沒有給出隊(duì)列應(yīng)該是如何實(shí)現(xiàn)的。通常,隊(duì)列的實(shí)現(xiàn)方式有兩種:一個(gè)是使用循環(huán)數(shù)組,一個(gè)是使用鏈表。使用循環(huán)數(shù)組時(shí)需要指出對(duì)頭head和隊(duì)尾tail;使用鏈表時(shí)也需要給出頭和尾:
class CircularArrayQueue<E> implements Queue<E> {CircularArrayQueue(int capacity){...}public void add(E element){...}public E remove(){...}private E[] elements;private int head;private int tail; }class LinkedListQueue<E> implements Queue<E> {LinkedListQueue(){...}public void add(E element){...}public E remove(){...}public int size(){...}private Link head;private Link tail; }上面的只是我們自己實(shí)現(xiàn)的簡(jiǎn)單的隊(duì)列,Java類庫中沒有這兩個(gè)類。當(dāng)需要使用隊(duì)列時(shí),可以使用LinkedList類,這個(gè)類實(shí)現(xiàn)了Queue接口。當(dāng)在程序中使用隊(duì)列時(shí),一旦構(gòu)建了集合就不需要知道究竟使用了哪種實(shí)現(xiàn)。因此,只有在構(gòu)建集合對(duì)象時(shí),使用具體的類才有意義。可以使用接口類型存放集合的引用:
Queue<Employee> employee=new CircularArrayQueue<>(100); employee.add(new Employee("Harry"));利用這種方式,一旦改變了想法,可以輕松地使用另一種不同的實(shí)現(xiàn)。只需要對(duì)程序的一個(gè)地方做出修改,即調(diào)用構(gòu)造器的地方。如果覺得LinkedListQueue是個(gè)更好的選擇,就可以這樣修改: Queue<Employee> employee=new LinkedListQueue<>(); employee.add(new Employee("Harry"));接口本身沒有給出具體的實(shí)現(xiàn)方式,因此也不能給出哪種實(shí)現(xiàn)更符合實(shí)際應(yīng)用。這樣,選擇哪種實(shí)現(xiàn)方式就需要由程序員選擇。Java類庫中實(shí)現(xiàn)了很多的類,每個(gè)類都有各自的特性以及適用場(chǎng)景,也許選擇了某個(gè)實(shí)現(xiàn)方式在某個(gè)特性上更加優(yōu)秀,但也可能在另一個(gè)特性上付出了代價(jià)。如何平衡好各個(gè)性能的代價(jià),需要類庫使用者自己把握。在上圖中,我們可以發(fā)現(xiàn)很多紅色的以Abstract開頭的類,這些類都是抽象類,這些類是為類庫的實(shí)現(xiàn)者設(shè)計(jì)的,這些類中實(shí)現(xiàn)了一些基本的方法。如果想要實(shí)現(xiàn)自己的隊(duì)列類,你會(huì)發(fā)現(xiàn)擴(kuò)展AbstractQueue類比實(shí)現(xiàn)Queue接口更方便。
3 Java類庫中的集合接口和迭代器接口
在Java類庫中,集合類的基本接口是Collection接口。這個(gè)接口中有如下兩個(gè)方法:
boolean add(E element); Iterator<E> iterator();當(dāng)然,除了這兩個(gè)方法外還有其它的方法,會(huì)在后面介紹。add方法用于向集合中添加元素,如果添加元素確實(shí)改變了集合就返回true,如果集合沒有發(fā)生變化就返回false。
iterator方法用于返回一個(gè)實(shí)現(xiàn)了Iterator接口的對(duì)象。可以使用這個(gè)迭代器對(duì)象依次訪問集合中的元素。
下面來介紹一下Java類庫中重要的迭代器。
Iterator迭代器接口有三個(gè)方法:
public interface Iterator<E> {E next();boolean hasNext();void remove(); }通過反復(fù)調(diào)用next方法,可以逐個(gè)訪問集合中的每個(gè)元素。但是如果達(dá)到了集合的末尾,next方法將拋出一個(gè)NoSuchElementException異常。因此,在調(diào)用next方法之前應(yīng)該調(diào)用hasNext方法。如果迭代器對(duì)象還有多個(gè)供訪問的元素,這個(gè)方法就返回true。因此,可以使用下面的方法訪問集合中的所有元素: Collection<String> c=...; Iterator<String> it=c.iterator(); while(it.hasNext()) {String element=it.next();do something with element }從Jave SE 5.0起,還可以使用for each循環(huán)訪問集合中的所有元素: for(String element : c) {do something with element }編譯器只是將這個(gè)for each循環(huán)翻譯為帶有迭代器的循環(huán)。for each循環(huán)可以與任何實(shí)現(xiàn)了Iterable接口的對(duì)象一起工作,這個(gè)接口只包含一個(gè)方法:
public interface Iterable<E> {Iterator<E> iterator(); }Collection接口擴(kuò)展了Iterable接口,所以對(duì)于標(biāo)準(zhǔn)庫中的任何集合都可以使用for each循環(huán)。需要注意的是,元素被訪問的順序取決于集合的類型。如果對(duì)ArrayList進(jìn)行迭代,迭代器將會(huì)從索引0開始,每迭代一次,索引值加1。然而,如果訪問HashSet中的元素,每個(gè)元素將會(huì)按照某種隨機(jī)的次序出現(xiàn)。雖然可以確定在迭代過程中能夠遍歷所有的元素,但卻無法預(yù)知元素被訪問的次序。
Java集合類庫中的迭代器與其它類庫中的迭代器在概念上有著重要的區(qū)別。C++的迭代器是根據(jù)數(shù)組索引建模的。如果給定這樣一個(gè)迭代器,就可以查看指定位置上的元素,就像知道數(shù)組索引i就可以查看數(shù)組元素a[i]一樣。不需要查找元素,就可以將迭代器向前移動(dòng)一個(gè)位置。這與不需要執(zhí)行查找操作就可以通過i++將數(shù)組索引向前移動(dòng)一樣。但是,Java迭代器不是這樣操作的。查找操作和位置變更是緊密相連的。查找一個(gè)元素的唯一方法是調(diào)用next,而在執(zhí)行查找操作的同時(shí),迭代器的位置隨之向前移動(dòng)。
即,可以將迭代器看做一個(gè)位置,這個(gè)位置在兩個(gè)元素之間。而next操作會(huì)改變這個(gè)位置。但調(diào)用next時(shí),迭代器就越過下一個(gè)元素,并返回剛剛越過的那個(gè)元素的引用。
Iterator接口的remove方法用于刪除上次調(diào)用next方法返回時(shí)的元素。也就是說,remove操作和next操作具有依賴性,如果沒有調(diào)用next方法而調(diào)用remove方法是非法的,會(huì)拋出一個(gè)IllegalStateException異常。下面是刪除集合中的第一個(gè)元素:
Iterator<String> it=c.iterator(); it.next(); it.remove();如果刪除兩個(gè)相鄰的元素,下面的方法是錯(cuò)誤的: it.remove(); it.remove();必須先調(diào)用next方法越過待刪除的元素: it.remove(); it.next(); it.remove();有一個(gè)不怎么恰當(dāng)?shù)谋扔?#xff0c;可以將迭代器看做光標(biāo): Collection<String> c=ArrayList<>(); c.add("a"); c.add("b"); c.add("c"); Iterator<String> it=c.iterator();這時(shí),迭代器的位置如下:?| a b c
當(dāng)調(diào)用next方法,光標(biāo)就會(huì)后移,然后返回剛才越過的元素:
it.next();此時(shí),迭代器的位置如下:a | b c
并返回元素a。
如果要?jiǎng)h除元素,刪除的行為就像后退鍵(Backspace)一樣,刪除光標(biāo)后面(以右為前)的元素:
it.remove();此時(shí),迭代器的位置如下:| b c
和后退鍵不同的是,如果迭代器的后面即使還有元素,沒有調(diào)用next方法也不能刪除。
由于Collection接口和Iterator接口都是泛型接口,可以編寫操作任何集合類型的實(shí)用方法。其實(shí),Collection接口中有很多方法:
public interface java.util.Collection<E> extends java.lang.Iterable<E> {public abstract int size();public abstract boolean isEmpty();public abstract boolean contains(java.lang.Object);public abstract java.util.Iterator<E> iterator();public abstract java.lang.Object[] toArray();public abstract <T> T[] toArray(T[]);public abstract boolean add(E);public abstract boolean remove(java.lang.Object);public abstract boolean containsAll(java.util.Collection<?>);public abstract boolean addAll(java.util.Collection<? extends E>);public abstract boolean removeAll(java.util.Collection<?>);public boolean removeIf(java.util.function.Predicate<? super E>);public abstract boolean retainAll(java.util.Collection<?>);public abstract void clear();public abstract boolean equals(java.lang.Object);public abstract int hashCode();public java.util.Spliterator<E> spliterator();public java.util.stream.Stream<E> stream();public java.util.stream.Stream<E> parallelStream(); }有很多的方法的含義都很明確,這里不做過多的解釋。當(dāng)然,如果實(shí)現(xiàn)Collection接口的每一個(gè)類都要實(shí)現(xiàn)這些例行方法將是一件很煩人的事。為了能夠讓實(shí)現(xiàn)者更容易地實(shí)現(xiàn)這個(gè)接口,Java類庫提供了一個(gè)AbstractCollection類,在這個(gè)類里提供了一些例行方法,這樣,一個(gè)具體的集合類就可以擴(kuò)展AbstractCollection類而不需要實(shí)現(xiàn)所有的例行方法了,并可以覆蓋里面的方法。
4 Java類庫中的接口
下圖給出了Java類庫中的所有接口:
其中,Collection和Map是集合框架的兩個(gè)主要接口,所有的集合類都實(shí)現(xiàn)了這兩個(gè)接口中的一個(gè)。Iterator接口和ListIterator接口是迭代器接口,而ListIterator接口提供了更豐富的操作,這個(gè)接口會(huì)在List列表中介紹。RandomAccess接口是一個(gè)標(biāo)簽接口,也就是說這個(gè)接口沒有任何方法,但是可以用這個(gè)接口標(biāo)注某個(gè)類,然后檢查一個(gè)類是否支持隨機(jī)訪問。
在隨后的介紹中,會(huì)詳細(xì)介紹這些接口的方法和使用。
5 Java類庫中的類
下面是Java類庫中的所有實(shí)現(xiàn)類:
其中紅顏色的是抽象類。可以明顯的分為兩個(gè)部分,一個(gè)集合(Collection),一個(gè)映射(Map)。
這些是常用的類,再加上一些專用的類,比如:EnumSet、LinkedHashSet、EnumMap、LinkedHashMap、IdentityHashMap和WeakHashMap,一共14類,它們的特點(diǎn)如下:
| 集合類型 | 描述 |
| ArrayList | 一種可以動(dòng)態(tài)增長(zhǎng)和伸縮的索引序列 |
| LinkedList | 一種可以在任何位置進(jìn)行高效插入和刪除操作的有序序列 |
| ArrayDeque | 一種用循環(huán)數(shù)組實(shí)現(xiàn)的雙端隊(duì)列 |
| HashSet | 一種沒有重復(fù)元素的無序集合 |
| TreeSet | 一種有序集 |
| EnumSet | 一種包含枚舉類型值的集 |
| LinkedHashSet | 一種可以記住元素插入順序的集 |
| PriorityQueue | 一種允許高效刪除最小元素的集合 |
| HashMap | 一種存儲(chǔ)鍵/值關(guān)聯(lián)的數(shù)據(jù)結(jié)構(gòu) |
| TreeMap | 一種鍵值有序排列的映射表 |
| EnumMap | 一種鍵值屬于枚舉類型的映射表 |
| LinkedHashMap | 一種可以記住鍵值項(xiàng)添加次序的映射表 |
| WeakHashMap | 一種其值無用武之地后可以被垃圾回收器回收的映射表 |
| IdentityHashMap | 一種使用==而不是equals比較鍵值的映射表 |
對(duì)于有序無序、元素可重復(fù)和元素不可重復(fù),特點(diǎn)總結(jié)如下,注意,對(duì)于Map,考察的是鍵的值是否可重復(fù):
6 更多
在后序的分析中,會(huì)給出這些具體類和集合的介紹:
2、Java集合(二):List列表
3、Java集合(三):Queue隊(duì)列
4、Java集合(四):Map映射
5、Java集合(五):Set集
6、Java集合(六):專用集合和遺留類
總結(jié)
以上是生活随笔為你收集整理的Java集合(一):Java集合概述的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Scala连接mongodb数据库
- 下一篇: 这个为什么不能阻止表单提交?? 财