集合類的介紹

一、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)了有序的 HashMapLinkedHashMapHashMap的一個(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)鏈表

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容