一、JAVA中的集合分類
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├HashTable
├HashMap
└WeakHashMap
Collection 和 Map 的區(qū)別
Collection類型者,每個(gè)位置只有一個(gè)元素。
Map類型者,持有 key-value pair,像個(gè)小型數(shù)據(jù)庫。
要注意:Collection、List、Set、Map都是接口,不能實(shí)例化。 繼承自它們的 ArrayList, Vector, HashTable, HashMap是具象class,這些才可被實(shí)例化。
二、Collection接口
Collection是最基本的集合接口,聲明了適用于JAVA集合(只包括Set和List)的通用方法。
??Collection接口的方法:
boolean add(Object o) :向集合中加入一個(gè)對(duì)象的引用
void clear() :刪除集合中所有的對(duì)象,即不再持有這些對(duì)象的引用
boolean remove(Object o) :從集合中刪除一個(gè)對(duì)象的引用
boolean isEmpty() :判斷集合是否為空
boolean contains(Object o) :判斷集合中是否持有特定對(duì)象的引用
Iterartor iterator() :返回一個(gè)Iterator對(duì)象,可以用來遍歷集合中的元素
int size() :返回集合中元素的數(shù)目
Object[] toArray() :返回一個(gè)數(shù)組,該數(shù)組中包括集合中的所有元素
注意:Collection沒有g(shù)et()方法來取得某個(gè)元素。只能通過iterator()遍歷元素!
1. Set(集合)
Set是最簡(jiǎn)單的一種集合,存放的是對(duì)象的引用。集合中的對(duì)象不按特定的方式排序,沒有重復(fù)的對(duì)象。Set接口主要有三個(gè)實(shí)現(xiàn)類:
- HashSet:HashSet類按照哈希算法來存取集合中的對(duì)象,存取速度較快,存入HashSet的對(duì)象必須定義hashCode()。
- TreeSet:TreeSet類底層為樹結(jié)構(gòu),對(duì)象以升序順序存儲(chǔ),訪問和遍歷的時(shí)間很快。
- LinkedHashSet:具有HashSet的查詢速度,且內(nèi)部使用鏈表維護(hù)元素的順序(插入的次序)。于是在使用迭代器遍歷Set時(shí),結(jié)果會(huì)按元素插入的次序顯示。
Set具有與Collection完全一樣的接口,因此沒有任何額外的功能,實(shí)際上Set就是Collection,只是行為不同。
??Set的add()方法是如何判斷是否有重復(fù)的對(duì)象在集合中的呢?由以下代碼實(shí)現(xiàn)
boolean isExists=false;
Iterator iterator=set.iterator();
while(it.hasNext()){
String oldStr=it.next();
if(newStr.equals(oldStr)){
isExists=true;
}
}
2. List(鏈表)
List是有序的Collection,集合中可以存放重復(fù)的對(duì)象。List接口主要有兩個(gè)實(shí)現(xiàn)類:
- ArrayList:代表長度可以改變的數(shù)組,可以對(duì)元素進(jìn)行隨機(jī)訪問,插入和刪除元素慢(數(shù)組的特征)。
- LinkedList:采用鏈表的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),插入和刪除速度快,訪問速度慢(鏈表的特征)。
要注意,因?yàn)長ist是一接口,所以不能被構(gòu)造,只能創(chuàng)建一個(gè)引用:
List list; //正確,list=null
List list = new ArrayList(); //正確
List list = new List(); //錯(cuò)誤的用法
同時(shí)也要注意下面兩種用法:
//下面這句程序創(chuàng)建了一個(gè)ArrayList對(duì)象后上溯到了List對(duì)象,此時(shí)它是一個(gè)List對(duì)象
//因此有些ArrayList有但是List沒有的屬性和方法,它就不能再用了。
List list = new ArrayList();
//下面這句程序創(chuàng)建的對(duì)象則保留了ArrayList的所有屬性。
ArrayList list = new ArrayList();
使用ArrayList list = new ArrayList(); 的問題就在于List有多個(gè)實(shí)現(xiàn)類,現(xiàn)在你用的是ArrayList,也許哪一天你需要換成其它的實(shí)現(xiàn)類,如 LinkedList或者Vector等等,這時(shí)你只要改變這一行就行了:List list = new LinkedList(),其它使用了list地方的代碼根本不需要改動(dòng)。 假設(shè)你開始用 ArrayList alist = new ArrayList(),這下你有的改了,特別是如果你使用了 ArrayList特有的方法和屬性。
??List為Collection添加了許多方法,使得能夠向List中間插入與移除元素(這只推 薦LinkedList使用),同時(shí)也提供了一些方法讓我們可以對(duì)List中的元素進(jìn)行隨機(jī)訪問。
??List的get(int index) 方法放回集合中由參數(shù)index指定的索引位置的對(duì)象,下標(biāo)從“0”開始。下面展示最基本的兩種檢索集合中的所有對(duì)象的方法:
//for循環(huán)和get()方法
for(int i=0; i<list.size();i++){
System.out.println(list.get(i));
}
// 使用 迭代器(Iterator)
Iterator it=list.iterator();
while(it.hashNext()){
System.out.println(it.next());
}
LinkList提供了一些功能方法,這些方法 (沒有在任何接口或基類中定義過)使得LinkedList可以當(dāng)作堆棧、隊(duì)列和雙向隊(duì)列使用:
addFirst()
addLast()
getFirst()
getLast()
removeFirst()
removeLast()
ListIterator listIterator() :和標(biāo)準(zhǔn)的Iterator接口比擬,ListIterator可以向List中添加對(duì)象,還可以實(shí)現(xiàn)逆向(順序向前)遍歷,這些都是Iterator沒有的
- 對(duì)于隨機(jī)訪問get和set,ArrayList優(yōu)于LinkedList,因?yàn)長inkedList要移動(dòng)指針
- 對(duì)于新增和刪除操作add和remove,LinedList比較占優(yōu)勢(shì),因?yàn)锳rrayList要移動(dòng)數(shù)據(jù)。
- 查找操作indexOf,lastIndexOf,contains等,兩者差不多。
三、Map接口
Map是一種把鍵對(duì)象和值對(duì)象映射的集合,它的每一個(gè)元素都包含一對(duì)鍵對(duì)象和值對(duì)象。Map中不允許有重復(fù)的鍵值。Map接口主要有兩個(gè)實(shí)現(xiàn)類:
1.HashMap
??HashMap的數(shù)據(jù)結(jié)構(gòu)和哈希表相同。下面看一下HashMap的存儲(chǔ):
int hash=key.hashCode(); //獲取key的hashCode,這個(gè)值是一個(gè)固定的int值
int index=hash%Entry[].length; //獲取數(shù)組下標(biāo):key的hash值對(duì)Entry數(shù)組長度進(jìn)行取余
Entry[index]=value;
HashMap會(huì)先用key的hash值來檢查是否發(fā)生了hash碰撞,也就是對(duì)應(yīng)的位置是否為空,這個(gè)沒問題。問題是當(dāng)發(fā)生了hash碰撞時(shí),就會(huì)比較該位置上存儲(chǔ)的每一個(gè)key是否與新存入的相等,如果相等就替換之,否則就在該位置增加一個(gè)值。很明顯,你代碼中的前后兩個(gè)key是相同的,所以后面的會(huì)替換掉前面的。
??此外介紹一下LinkedHashMap
。我們都知道HashMap是無序的,HashMap在put時(shí)是根據(jù) key 的 hashcode 進(jìn)行 hash 然后放入對(duì)應(yīng)的地方。所以在按照一定順序 put 進(jìn) HashMap 中,然后遍歷出 HashMap 的順序跟 put 的順序不同(除非在 put 的時(shí)候 key 已經(jīng)按照 hashcode 排序號(hào)了,這種幾率非常小)。JAVA 在 JDK1.4 以后提供了 LinkedHashMap
來幫助我們實(shí)現(xiàn)了有序的 HashMap
:LinkedHashMap
是HashMap
的一個(gè)子類,它保留插入的順序,如果需要輸出的順序和輸入時(shí)的相同,那么就選用 LinkedHashMap。我們來通過一個(gè)例子看看如何實(shí)現(xiàn)LinkedHashMap。
public static void main(String[] args) {
ap<String, String> map = new LinkedHashMap<String, String>();
map.put("apple", "蘋果");
map.put("watermelon", "西瓜");
map.put("banana", "香蕉");
map.put("peach", "桃子");
map.get("banana");
map.get("apple");
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
看一下控制臺(tái)的輸出:
apple=蘋果
watermelon=西瓜
banana=香蕉
peach=桃子
2.HashTable
??HashTable幾乎可以等價(jià)于HashMap,但是HashMap是非synchronized,而Hashtable是synchronized,這意味著Hashtable是線程安全的,多個(gè)線程可以共享一個(gè)Hashtable;而如果沒有正確的同步的話,多個(gè)線程是不能共享HashMap的。 sychronized意味著在一次僅有一個(gè)線程能夠更改Hashtable。就是說任何線程要更新Hashtable時(shí)要首先獲得同步鎖。
Map常用方法
【Map常用方法】
Object get(Object key) :返回與給定“鍵”相關(guān)聯(lián)的“值”
Object put(Object key, Object value): 向集合中加入元素
Object remove(Object key): 刪除與KEY相關(guān)的元素
void clear():從映像中刪除所有映射
【遍歷Map的兩種方法】
keySet():keySet是鍵的集合,Set里面的類型即key的類型。
entrySet():entrySet是 鍵-值 對(duì)的集合,Set里面的類型是Map.Entry。
//keySet
Map map=new HashMap();
Iterator it=map.keySet().iterator();
Object key;
Object value;
while(it.hasNext()){
key=it.next();
value=map.get(key);
System.out.println(key+":"+value);
}
//entrySet()
Map map=new HashMap();
Iterator it=map.entrySet().iterator();
Object key;
Object value;
while(it.hasNext()){
Map.Entry entry = (Map.Entry)it.next();
key=entry.getKey();
value=entry.getValue();
System.out.println(key+"="+value);
}
四、數(shù)組和集合的比較
世間上本來沒有集合,有人想要,所以有了集合。有人想有可以自動(dòng)擴(kuò)展的數(shù)組,所以有了List ;有的人想有沒有重復(fù)的數(shù)組,所以有了set;有人想有自動(dòng)排序的組數(shù),所以有了TreeSet、TreeList、Tree** ....。
??幾乎所有的集合都是基于數(shù)組來實(shí)現(xiàn)的。因?yàn)榧鲜菍?duì)數(shù)組做的封裝,所以數(shù)組永遠(yuǎn)比任何一個(gè)集合要快,但任何一個(gè)集合,比數(shù)組提供的功能要多。
數(shù)組與集合的對(duì)比:
1.數(shù)組要聲明了它容納的元素的類型,并且同一個(gè)數(shù)組織能存放類型一樣的數(shù)據(jù);而集合元素類型是object,即不用聲明元素類型。
2.一個(gè)數(shù)組實(shí)例具有固定的大小,不能伸縮;集合則可根據(jù)需要?jiǎng)討B(tài)改變大小。
3.數(shù)組要有整數(shù)下標(biāo)才能訪問特定的元素。集合也是數(shù)據(jù)列表卻不使用下標(biāo)訪問。
4.數(shù)組是一種可讀/可寫數(shù)據(jù)結(jié)構(gòu),沒有辦法創(chuàng)建一個(gè)只讀數(shù)組。然而集合可以提供ReadOnly方法,以只讀方式來使用集合。
Java中有一個(gè)Arrays類,專門用來操作array,但是Array還有一個(gè)缺點(diǎn)是,無法判斷其中實(shí)際存有多少元素,length只是告訴我們array的容量。
??如果我們想將一個(gè)數(shù)組轉(zhuǎn)化為一個(gè)List對(duì)象,可以使用Arrays.asList(),這個(gè)方法會(huì)返回一個(gè)ArrayList類型的對(duì)象,這個(gè)ArrayList并不是java.util.ArrayList,它是一個(gè)Arrays類中的重新定義的內(nèi)部類!這個(gè)生成的List,它是固定長度的,如果對(duì)其進(jìn)行add或者remove的操作,會(huì)拋出UnsupportedOperationException。我們來看看這個(gè)ArrayList的實(shí)現(xiàn):
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private Object[] a;
ArrayList(E[] array) {
if (array==null)
throw new NullPointerException();
a = array;
}
public int size() {
return a.length;
}
public Object[] toArray() {
return (Object[])a.clone();
}
public E get(int index) {
return (E)a[index];
}
public E set(int index, E element) {
Object oldValue = a[index];
a[index] = element;
return (E)oldValue;
}
public int indexOf(Object o) {
if (o==null) {
for (int i=0; i<a.length; i++)
if (a[i]==null)
return i;
} else {
for (int i=0; i<a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
public boolean contains(Object o) {
return indexOf(o) != -1;
}
}
在這里,傳遞基本類型數(shù)組時(shí)強(qiáng)烈建議轉(zhuǎn)為其封裝類對(duì)象的數(shù)組 int ->Integer ,long->Long ,不然不管數(shù)組里面有多少個(gè)元素,使用Arrays.asList(array).size()得出來的結(jié)果都為1。
五、迭代器
Iterator接口聲明了如下方法:
hasNext():判斷集合中元素是否遍歷完畢,如果沒有,就返回true
next() :返回下一個(gè)元素
remove() :從集合中刪除上一個(gè)有next()方法返回的元素。
而上面我們講到的LinkList中的ListIterator迭代器包含的方法有:
add(E e): 將指定的元素插入列表,插入位置為迭代器當(dāng)前位置之前
hasNext():以正向遍歷列表時(shí),如果列表迭代器后面還有元素,則返回 true,否則返回false
hasPrevious():如果以逆向遍歷列表,列表迭代器前面還有元素,則返回 true,否則返回false
next():返回列表中ListIterator指向位置后面的元素
nextIndex():返回列表中ListIterator所需位置后面元素的索引
previous():返回列表中ListIterator指向位置前面的元素
previousIndex():返回列表中ListIterator所需位置前面元素的索引
remove():從列表中刪除next()或previous()返回的最后一個(gè)元素(有點(diǎn)拗口,意思就是對(duì)迭代器使用hasNext()方法時(shí),刪除ListIterator指向位置后面的元素;當(dāng)對(duì)迭代器使用hasPrevious()方法時(shí),刪除ListIterator指向位置前面的元素)
set(E e):從列表中將next()或previous()返回的最后一個(gè)元素返回的最后一個(gè)元素更改為指定元素e
六、集合中的數(shù)據(jù)結(jié)構(gòu)
(1)哈希表
詳情可以看我寫的哈希詳解,具體的將不再描述。
(2)鏈表