喫透JAVA集合框架(超級詳細)(綜述)

一 集合框架圖
在這裏插入圖片描述
簡化圖一:
在這裏插入圖片描述
簡化圖二:
在這裏插入圖片描述
說明:對於以上的框架圖有如下幾點說明

1、所有集合類都位於java.util包下。Java的集合類主要由兩個接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,這兩個接口又包含了一些子接口或實現類。

2、集合接口:6個接口(短虛線表示),表示不同集合類型,是集合框架的基礎。

3、抽象類:5個抽象類(長虛線表示),對集合接口的部分實現。可擴展爲自定義集合類。

4、實現類:8個實現類(實線表示),對接口的具體實現。

5、Collection 接口是一組允許重複的對象。

6、Set 接口繼承 Collection,集合元素不重複。

7、List 接口繼承 Collection,允許重複,維護元素插入順序。

8、Map接口是鍵-值對象,與Collection接口沒有什麼關係。

9、Set、List和Map可以看做集合的三大類:

List集合是有序集合,集合中的元素可以重複,訪問集合中的元素可以根據元素的索引來訪問。
Set集合是無序集合,集合中的元素不可以重複,訪問集合中的元素只能根據元素本身來訪問(也是集合裏元素不允許重複的原因)。
Map集合中保存Key-value對形式的元素,訪問時只能根據每項元素的key來訪問其value
在這裏插入圖片描述
在這裏插入圖片描述
二 整體認識

在這裏插入圖片描述

大致說明:

看上面的框架圖,先抓住它的主幹,即Collection和Map。

1、Collection是一個接口,是高度抽象出來的集合,它包含了集合的基本操作和屬性。Collection包含了List和Set兩大分支。

List是一個有序的隊列,每一個元素都有它的索引。第一個元素的索引值是0。List的實現類有LinkedList, ArrayList, Vector, Stack。
Set是一個不允許有重複元素的集合。Set的實現類有HastSet和TreeSet。HashSet依賴於HashMap,它實際上是通過HashMap實現的;TreeSet依賴於TreeMap,它實際上是通過TreeMap實現的。
2、Map是一個映射接口,即key-value鍵值對。Map中的每一個元素包含“一個key”和“key對應的value”。AbstractMap是個抽象類,它實現了Map接口中的大部分API。而HashMap,TreeMap,WeakHashMap都是繼承於AbstractMap。Hashtable雖然繼承於Dictionary,但它實現了Map接口。

3、接下來,再看Iterator。它是遍歷集合的工具,即我們通常通過Iterator迭代器來遍歷集合。我們說Collection依賴於Iterator,是因爲Collection的實現類都要實現iterator()函數,返回一個Iterator對象。ListIterator是專門爲遍歷List而存在的。

4、再看Enumeration,它是JDK 1.0引入的抽象類。作用和Iterator一樣,也是遍歷集合;但是Enumeration的功能要比Iterator少。在上面的框圖中,Enumeration只能在Hashtable, Vector, Stack中使用。

5、最後,看Arrays和Collections。它們是操作數組、集合的兩個工具類。

有了上面的整體框架之後,我們接下來對每個類分別進行分析。
三 Collection
在這裏插入圖片描述
Collection接口是處理對象集合的根接口,其中定義了很多對元素進行操作的方法。**Collection接口有兩個主要的子接口List和Set,**注意Map不是Collection的子接口,這個要牢記。

Collection接口中的方法如下
在這裏插入圖片描述
其中,有幾個比較常用的方法,比如方法add()添加一個元素到集合中,addAll()將指定集合中的所有元素添加到集合中,contains()方法檢測集合中是否包含指定的元素,toArray()方法返回一個表示集合的數組。

另外,Collection中有一個iterator()函數,它的作用是返回一個Iterator接口。通常,我們通過Iterator迭代器來遍歷集合。ListIterator是List接口所特有的,在List接口中,通過ListIterator()返回一個ListIterator對象。

Collection接口有兩個常用的子接口,下面詳細介紹。

1.List接口

List集合代表一個有序集合,集合中每個元素都有其對應的順序索引。List集合允許使用重複元素,可以通過索引來訪問指定位置的集合元素。

List接口繼承於Collection接口,它可以定義一個允許重複的有序集合。因爲List中的元素是有序的,所以我們可以通過使用索引(元素在List中的位置,類似於數組下標)來訪問List中的元素,這類似於Java的數組。

List接口爲Collection直接接口。List所代表的是有序的Collection,即它用某種特定的插入順序來維護元素順序。用戶可以對列表中每個元素的插入位置進行精確地控制,同時可以根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。實現List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。

(1)ArrayList

ArrayList是一個動態數組,也是我們最常用的集合。它允許任何符合規則的元素插入甚至包括null。每一個ArrayList都有一個初始容量(10),該容量代表了數組的大小。隨着容器中的元素不斷增加,容器的大小也會隨着增加。在每次向容器中增加元素的同時都會進行容量檢查,當快溢出時,就會進行擴容操作。所以如果我們明確所插入元素的多少,最好指定一個初始容量值,避免過多的進行擴容操作而浪費時間、效率。

size、isEmpty、get、set、iterator和 listIterator 操作都以固定時間運行。add 操作以分攤的固定時間運行,也就是說,添加 n 個元素需要 O(n) 時間(由於要考慮到擴容,所以這不只是添加元素會帶來分攤固定時間開銷那樣簡單)。

ArrayList擅長於隨機訪問。同時ArrayList是非同步的。

(2)LinkedList

同樣實現List接口的LinkedList與ArrayList不同,ArrayList是一個動態數組,而LinkedList是一個雙向鏈表。所以它除了有ArrayList的基本操作方法外還額外提供了get,remove,insert方法在LinkedList的首部或尾部。

由於實現的方式不同,LinkedList不能隨機訪問,它所有的操作都是要按照雙重鏈表的需要執行。在列表中索引的操作將從開頭或結尾遍歷列表(從靠近指定索引的一端)。這樣做的好處就是可以通過較低的代價在List中進行插入和刪除操作。

與ArrayList一樣,LinkedList也是非同步的。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List:

List list = Collections.synchronizedList(new LinkedList(…));
(3)Vector

與ArrayList相似,但是Vector是同步的。所以說Vector是線程安全的動態數組。它的操作與ArrayList幾乎一樣。

(4)Stack

Stack繼承自Vector,實現一個後進先出的堆棧。Stack提供5個額外的方法使得Vector得以被當作堆棧使用。基本的push和pop 方法,還有peek方法得到棧頂的元素,empty方法測試堆棧是否爲空,search方法檢測一個元素在堆棧中的位置。Stack剛創建後是空棧。
在這裏插入圖片描述
2.Set接口

Set是一種不包括重複元素的Collection。它維持它自己的內部排序,所以隨機訪問沒有任何意義。與List一樣,它同樣允許null的存在但是僅有一個。由於Set接口的特殊性,所有傳入Set集合中的元素都必須不同,同時要注意任何可變對象,如果在對集合中元素進行操作時,導致e1.equals(e2)==true,則必定會產生某些問題。Set接口有三個具體實現類,分別是散列集HashSet、鏈式散列集LinkedHashSet和樹形集TreeSet。

Set是一種不包含重複的元素的Collection,無序,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。

需要注意的是:雖然Set中元素沒有順序,但是元素在set中的位置是由該元素的HashCode決定的,其具體位置其實是固定的。

此外需要說明一點,在set接口中的不重複是有特殊要求的。
舉一個例子:對象A和對象B,本來是不同的兩個對象,正常情況下它們是能夠放入到Set裏面的,但是如果對象A和B的都重寫了hashcode和equals方法,並且重寫後的hashcode和equals方法是相同的話。那麼A和B是不能同時放入到Set集合中去的,也就是Set集合中的去重和hashcode與equals方法直接相關。

爲了更好地理解,請看下面的例子:

public class Test{
public static void main(String[] args) {
Set set=new HashSet();
set.add(“Hello”);
set.add(“world”);
set.add(“Hello”);
System.out.println(“集合的尺寸爲:”+set.size());
System.out.println(“集合中的元素爲:”+set.toString());
}
}
運行結果:

集合的尺寸爲:2
集合中的元素爲:[world, Hello]
分析:由於String類中重寫了hashcode和equals方法,用來比較指向的字符串對象所存儲的字符串是否相等。所以這裏的第二個Hello是加不進去的。

再看一個例子:

public class TestSet {

public static void main(String[] args){

    Set<String> books = new HashSet<String>();
    //添加一個字符串對象
    books.add(new String("Struts2權威指南"));

    //再次添加一個字符串對象,
    //因爲兩個字符串對象通過equals方法比較相等,所以添加失敗,返回false
    boolean result = books.add(new String("Struts2權威指南"));

    System.out.println(result);

    //下面輸出看到集合只有一個元素
    System.out.println(books);    

}

}
運行結果:

false
[Struts2權威指南]
說明:程序中,book集合兩次添加的字符串對象明顯不是一個對象(程序通過new關鍵字來創建字符串對象),當使用==運算符判斷返回false,使用equals方法比較返回true,所以不能添加到Set集合中,最後只能輸出一個元素。

(1)HashSet

HashSet 是一個沒有重複元素的集合。它是由HashMap實現的,不保證元素的順序(這裏所說的沒有順序是指:元素插入的順序與輸出的順序不一致),而且HashSet允許使用null 元素。HashSet是非同步的,如果多個線程同時訪問一個哈希set,而其中至少一個線程修改了該set,那麼它必須保持外部同步。 HashSet按Hash算法來存儲集合的元素,因此具有很好的存取和查找性能。

HashSet的實現方式大致如下,通過一個HashMap存儲元素,元素是存放在HashMap的Key中,而Value統一使用一個Object對象。

HashSet使用和理解中容易出現的誤區:

a.HashSet中存放null值。HashSet中是允許存入null值的,但是在HashSet中僅僅能夠存入一個null值。

b.HashSet中存儲元素的位置是固定的。HashSet中存儲的元素的是無序的,這個沒什麼好說的,但是由於HashSet底層是基於Hash算法實現的,使用了hashcode,所以HashSet中相應的元素的位置是固定的。

c.必須小心操作可變對象(Mutable Object)。如果一個Set中的可變元素改變了自身狀態導致Object.equals(Object)=true將導致一些問題。

(2)LinkedHashSet

LinkedHashSet繼承自HashSet,其底層是基於LinkedHashMap來實現的,有序,非同步。LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的存儲位置,但是它同時使用鏈表維護元素的次序。這樣使得元素看起來像是以插入順序保存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的添加順序訪問集合的元素。

(3)TreeSet

TreeSet是一個有序集合,其底層是基於TreeMap實現的,非線程安全。TreeSet可以確保集合元素處於排序狀態。TreeSet支持兩種排序方式,自然排序和定製排序,其中自然排序爲默認的排序方式。當我們構造TreeSet時,若使用不帶參數的構造函數,則TreeSet的使用自然比較器;若用戶需要使用自定義的比較器,則需要使用帶比較器的參數。

注意:TreeSet集合不是通過hashcode和equals函數來比較元素的.它是通過compare或者comparaeTo函數來判斷元素是否相等.compare函數通過判斷兩個對象的id,相同的id判斷爲重複元素,不會被加入到集合中。
2.Set接口

Set是一種不包括重複元素的Collection。它維持它自己的內部排序,所以隨機訪問沒有任何意義。與List一樣,它同樣允許null的存在但是僅有一個。由於Set接口的特殊性,所有傳入Set集合中的元素都必須不同,同時要注意任何可變對象,如果在對集合中元素進行操作時,導致e1.equals(e2)==true,則必定會產生某些問題。Set接口有三個具體實現類,分別是散列集HashSet、鏈式散列集LinkedHashSet和樹形集TreeSet。

Set是一種不包含重複的元素的Collection,無序,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。

需要注意的是:雖然Set中元素沒有順序,但是元素在set中的位置是由該元素的HashCode決定的,其具體位置其實是固定的。

此外需要說明一點,在set接口中的不重複是有特殊要求的。
舉一個例子:對象A和對象B,本來是不同的兩個對象,正常情況下它們是能夠放入到Set裏面的,但是如果對象A和B的都重寫了hashcode和equals方法,並且重寫後的hashcode和equals方法是相同的話。那麼A和B是不能同時放入到Set集合中去的,也就是Set集合中的去重和hashcode與equals方法直接相關。

爲了更好地理解,請看下面的例子:

public class Test{
public static void main(String[] args) {
Set set=new HashSet();
set.add(“Hello”);
set.add(“world”);
set.add(“Hello”);
System.out.println(“集合的尺寸爲:”+set.size());
System.out.println(“集合中的元素爲:”+set.toString());
}
}
運行結果:

集合的尺寸爲:2
集合中的元素爲:[world, Hello]
分析:由於String類中重寫了hashcode和equals方法,用來比較指向的字符串對象所存儲的字符串是否相等。所以這裏的第二個Hello是加不進去的。

再看一個例子:

public class TestSet {

public static void main(String[] args){

    Set<String> books = new HashSet<String>();
    //添加一個字符串對象
    books.add(new String("Struts2權威指南"));

    //再次添加一個字符串對象,
    //因爲兩個字符串對象通過equals方法比較相等,所以添加失敗,返回false
    boolean result = books.add(new String("Struts2權威指南"));

    System.out.println(result);

    //下面輸出看到集合只有一個元素
    System.out.println(books);    

}

}
運行結果:

false
[Struts2權威指南]
說明:程序中,book集合兩次添加的字符串對象明顯不是一個對象(程序通過new關鍵字來創建字符串對象),當使用==運算符判斷返回false,使用equals方法比較返回true,所以不能添加到Set集合中,最後只能輸出一個元素。

(1)HashSet

HashSet 是一個沒有重複元素的集合。它是由HashMap實現的,不保證元素的順序(這裏所說的沒有順序是指:元素插入的順序與輸出的順序不一致),而且HashSet允許使用null 元素。HashSet是非同步的,如果多個線程同時訪問一個哈希set,而其中至少一個線程修改了該set,那麼它必須保持外部同步。 HashSet按Hash算法來存儲集合的元素,因此具有很好的存取和查找性能。

HashSet的實現方式大致如下,通過一個HashMap存儲元素,元素是存放在HashMap的Key中,而Value統一使用一個Object對象。

HashSet使用和理解中容易出現的誤區:

a.HashSet中存放null值。HashSet中是允許存入null值的,但是在HashSet中僅僅能夠存入一個null值。

b.HashSet中存儲元素的位置是固定的。HashSet中存儲的元素的是無序的,這個沒什麼好說的,但是由於HashSet底層是基於Hash算法實現的,使用了hashcode,所以HashSet中相應的元素的位置是固定的。

c.必須小心操作可變對象(Mutable Object)。如果一個Set中的可變元素改變了自身狀態導致Object.equals(Object)=true將導致一些問題。

(2)LinkedHashSet

LinkedHashSet繼承自HashSet,其底層是基於LinkedHashMap來實現的,有序,非同步。LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的存儲位置,但是它同時使用鏈表維護元素的次序。這樣使得元素看起來像是以插入順序保存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的添加順序訪問集合的元素。

(3)TreeSet

TreeSet是一個有序集合,其底層是基於TreeMap實現的,非線程安全。TreeSet可以確保集合元素處於排序狀態。TreeSet支持兩種排序方式,自然排序和定製排序,其中自然排序爲默認的排序方式。當我們構造TreeSet時,若使用不帶參數的構造函數,則TreeSet的使用自然比較器;若用戶需要使用自定義的比較器,則需要使用帶比較器的參數。

注意:TreeSet集合不是通過hashcode和equals函數來比較元素的.它是通過compare或者comparaeTo函數來判斷元素是否相等.compare函數通過判斷兩個對象的id,相同的id判斷爲重複元素,不會被加入到集合中。
在這裏插入圖片描述
五、Iterator 與 ListIterator詳解

1.Iterator

Iterator的定義如下:

public interface Iterator {}
Iterator是一個接口,它是集合的迭代器。集合可以通過Iterator去遍歷集合中的元素。

Iterator提供的API接口如下:

boolean hasNext():判斷集合裏是否存在下一個元素。如果有,hasNext()方法返回 true。
Object next():返回集合裏下一個元素。
void remove():刪除集合裏上一次next方法返回的元素。
使用示例:

public class IteratorExample {
public static void main(String[] args) {
ArrayList a = new ArrayList();
a.add(“aaa”);
a.add(“bbb”);
a.add(“ccc”);
System.out.println("Before iterate : " + a);
Iterator it = a.iterator();
while (it.hasNext()) {
String t = it.next();
if (“bbb”.equals(t)) {
it.remove();
}
}
System.out.println("After iterate : " + a);
}
}
輸出結果如下:

Before iterate : [aaa, bbb, ccc]
After iterate : [aaa, ccc]
注意:

Iterator只能單向移動。
Iterator.remove()是唯一安全的方式來在迭代過程中修改集合;如果在迭代過程中以任何其它的方式修改了基本集合將會產生未知的行爲。而且每調用一次next()方法,remove()方法只能被調用一次,如果違反這個規則將拋出一個異常。
2.ListIterator

ListIterator是一個功能更加強大的迭代器, 它繼承於Iterator接口,只能用於各種List類型的訪問。可以通過調用listIterator()方法產生一個指向List開始處的ListIterator, 還可以調用listIterator(n)方法創建一個一開始就指向列表索引爲n的元素處的ListIterator。

ListIterator接口定義如下:

public interface ListIterator extends Iterator {
boolean hasNext();

E next();

boolean hasPrevious();

E previous();

int nextIndex();

int previousIndex();

void remove();

void set(E e);

void add(E e);

}
由以上定義我們可以推出ListIterator可以:

雙向移動(向前/向後遍歷).
產生相對於迭代器在列表中指向的當前位置的前一個和後一個元素的索引.
可以使用set()方法替換它訪問過的最後一個元素.
可以使用add()方法在next()方法返回的元素之前或previous()方法返回的元素之後插入一個元素.
使用示例:

public class ListIteratorExample {

public static void main(String[] args) {
    ArrayList<String> a = new ArrayList<String>();
    a.add("aaa");
    a.add("bbb");
    a.add("ccc");
    System.out.println("Before iterate : " + a);
    ListIterator<String> it = a.listIterator();
    while (it.hasNext()) {
        System.out.println(it.next() + ", " + it.previousIndex() + ", " + it.nextIndex());
    }
    while (it.hasPrevious()) {
        System.out.print(it.previous() + " ");
    }
    System.out.println();
    it = a.listIterator(1);
    while (it.hasNext()) {
        String t = it.next();
        System.out.println(t);
        if ("ccc".equals(t)) {
            it.set("nnn");
        } else {
            it.add("kkk");
        }
    }
    System.out.println("After iterate : " + a);
}

}
輸出結果如下:

Before iterate : [aaa, bbb, ccc]
aaa, 0, 1
bbb, 1, 2
ccc, 2, 3
ccc bbb aaa
bbb
ccc
After iterate : [aaa, bbb, kkk, nnn]
六、異同點

1.ArrayList和LinkedList

ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。
對於隨機訪問get和set,ArrayList絕對優於LinkedList,因爲LinkedList要移動指針。
對於新增和刪除操作add和remove,LinedList比較佔優勢,因爲ArrayList要移動數據。
這一點要看實際情況的。若只對單條數據插入或刪除,ArrayList的速度反而優於LinkedList。但若是批量隨機的插入刪除數據,LinkedList的速度大大優於ArrayList. 因爲ArrayList每插入一條數據,要移動插入點及之後的所有數據。

2.HashTable與HashMap

相同點:

都實現了Map、Cloneable、java.io.Serializable接口。
都是存儲"鍵值對(key-value)"的散列表,而且都是採用拉鍊法實現的。
不同點:

(1)歷史原因:HashTable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現 。

(2)同步性:HashTable是線程安全的,也就是說是同步的,而HashMap是線程序不安全的,不是同步的 。

(3)對null值的處理:HashMap的key、value都可爲null,HashTable的key、value都不可爲null 。

(4)基類不同:HashMap繼承於AbstractMap,而Hashtable繼承於Dictionary。

Dictionary是一個抽象類,它直接繼承於Object類,沒有實現任何接口。Dictionary類是JDK 1.0的引入的。雖然Dictionary也支持“添加key-value鍵值對”、“獲取value”、“獲取大小”等基本操作,但它的API函數比Map少;而且Dictionary一般是通過Enumeration(枚舉類)去遍歷,Map則是通過Iterator(迭代M器)去遍歷。 然而由於Hashtable也實現了Map接口,所以,它即支持Enumeration遍歷,也支持Iterator遍歷。
AbstractMap是一個抽象類,它實現了Map接口的絕大部分API函數;爲Map的具體實現類提供了極大的便利。它是JDK 1.2新增的類。
(5)支持的遍歷種類不同:HashMap只支持Iterator(迭代器)遍歷。而Hashtable支持Iterator(迭代器)和Enumeration(枚舉器)兩種方式遍歷。

3.HashMap、Hashtable、LinkedHashMap和TreeMap比較

Hashmap 是一個最常用的Map,它根據鍵的HashCode 值存儲數據,根據鍵可以直接獲取它的值,具有很快的訪問速度。遍歷時,取得數據的順序是完全隨機的。HashMap最多隻允許一條記錄的鍵爲Null;允許多條記錄的值爲Null;HashMap不支持線程的同步,即任一時刻可以有多個線程同時寫HashMap;可能會導致數據的不一致。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力。

Hashtable 與 HashMap類似,不同的是:它不允許記錄的鍵或者值爲空;它支持線程的同步,即任一時刻只有一個線程能寫Hashtable,因此也導致了Hashtale在寫入時會比較慢。

LinkedHashMap保存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先得到的記錄肯定是先插入的,也可以在構造時用帶參數,按照應用次數排序。在遍歷的時候會比HashMap慢,不過有種情況例外,當HashMap容量很大,實際數據較少時,遍歷起來可能會比LinkedHashMap慢,因爲LinkedHashMap的遍歷速度只和實際數據有關,和容量無關,而HashMap的遍歷速度和他的容量有關。

如果需要輸出的順序和輸入的相同,那麼用LinkedHashMap可以實現,它還可以按讀取順序來排列,像連接池中可以應用。LinkedHashMap實現與HashMap的不同之處在於,後者維護着一個運行於所有條目的雙重鏈表。此鏈接列表定義了迭代順序,該迭代順序可以是插入順序或者是訪問順序。對於LinkedHashMap而言,它繼承與HashMap、底層使用哈希表與雙向鏈表來保存所有元素。其基本操作與父類HashMap相似,它通過重寫父類相關的方法,來實現自己的鏈接列表特性。

TreeMap實現SortMap接口,內部實現是紅黑樹。能夠把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也可以指定排序的比較器,當用Iterator 遍歷TreeMap時,得到的記錄是排過序的。TreeMap不允許key的值爲null。非同步的。

一般情況下,我們用的最多的是HashMap,HashMap裏面存入的鍵值對在取出的時候是隨機的,它根據鍵的HashCode值存儲數據,根據鍵可以直接獲取它的值,具有很快的訪問速度。在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。

TreeMap取出來的是排序後的鍵值對。但如果您要按自然順序或自定義順序遍歷鍵,那麼TreeMap會更好。

LinkedHashMap 是HashMap的一個子類,如果需要輸出的順序和輸入的相同,那麼用LinkedHashMap可以實現,它還可以按讀取順序來排列,像連接池中可以應用。

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.TreeMap;

public class MapTest {

public static void main(String[] args) {

    //HashMap
    HashMap<String,String> hashMap = new HashMap();
    hashMap.put("4", "d");
    hashMap.put("3", "c");
    hashMap.put("2", "b");
    hashMap.put("1", "a");

    Iterator<String> iteratorHashMap = hashMap.keySet().iterator();

    System.out.println("HashMap-->");

    while (iteratorHashMap.hasNext()){

        Object key1 = iteratorHashMap.next();
        System.out.println(key1 + "--" + hashMap.get(key1));
    }

    //LinkedHashMap
    LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap();
    linkedHashMap.put("4", "d");
    linkedHashMap.put("3", "c");
    linkedHashMap.put("2", "b");
    linkedHashMap.put("1", "a");

    Iterator<String> iteratorLinkedHashMap = linkedHashMap.keySet().iterator();

    System.out.println("LinkedHashMap-->");

    while (iteratorLinkedHashMap.hasNext()){

        Object key2 = iteratorLinkedHashMap.next();
        System.out.println(key2 + "--" + linkedHashMap.get(key2));
    }

    //TreeMap
    TreeMap<String,String> treeMap = new TreeMap();
    treeMap.put("4", "d");
    treeMap.put("3", "c");
    treeMap.put("2", "b");
    treeMap.put("1", "a");

    Iterator<String> iteratorTreeMap = treeMap.keySet().iterator();

    System.out.println("TreeMap-->");

    while (iteratorTreeMap.hasNext()){

        Object key3 = iteratorTreeMap.next();
        System.out.println(key3 + "--" + treeMap.get(key3));
    }

}

}
輸出結果:

HashMap–>
3–c
2–b
1–a
4–d
LinkedHashMap–>
4–d
3–c
2–b
1–a
TreeMap–>
1–a
2–b
3–c
4–d
4.HashSet、LinkedHashSet、TreeSet比較

Set接口

Set不允許包含相同的元素,如果試圖把兩個相同元素加入同一個集合中,add方法返回false。

Set判斷兩個對象相同不是使用==運算符,而是根據equals方法。也就是說,只要兩個對象用equals方法比較返回true,Set就不會接受這兩個對象。

HashSet

HashSet有以下特點:

不能保證元素的排列順序,順序有可能發生變化。
不是同步的。
集合元素可以是null,但只能放入一個null。
當向HashSet結合中存入一個元素時,HashSet會調用該對象的hashCode()方法來得到該對象的hashCode值,然後根據 hashCode值來決定該對象在HashSet中存儲位置。簡單的說,HashSet集合判斷兩個元素相等的標準是兩個對象通過equals方法比較相等,並且兩個對象的hashCode()方法返回值也相等。

注意,如果要把一個對象放入HashSet中,重寫該對象對應類的equals方法,也應該重寫其hashCode()方法。其規則是如果兩個對象通過equals方法比較返回true時,其hashCode也應該相同。另外,對象中用作equals比較標準的屬性,都應該用來計算 hashCode的值。
LinkedHashSet

LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的存儲位置,但是它同時使用鏈表維護元素的次序。這樣使得元素看起來像是以插入順序保存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的添加順序訪問集合的元素。

LinkedHashSet在迭代訪問Set中的全部元素時,性能比HashSet好,但是插入時性能稍微遜色於HashSet。

TreeSet類

TreeSet是SortedSet接口的唯一實現類,TreeSet可以確保集合元素處於排序狀態。TreeSet支持兩種排序方式,自然排序和定製排序,其中自然排序爲默認的排序方式。向TreeSet中加入的應該是同一個類的對象。

TreeSet判斷兩個對象不相等的方式是兩個對象通過equals方法返回false,或者通過CompareTo方法比較沒有返回0。

自然排序

自然排序使用要排序元素的CompareTo(Object obj)方法來比較元素之間大小關係,然後將元素按照升序排列。

Java提供了一個Comparable接口,該接口裏定義了一個compareTo(Object obj)方法,該方法返回一個整數值,實現了該接口的對象就可以比較大小。obj1.compareTo(obj2)方法如果返回0,則說明被比較的兩個對象相等,如果返回一個正數,則表明obj1大於obj2,如果是負數,則表明obj1小於obj2。如果我們將兩個對象的equals方法總是返回true,則這兩個對象的compareTo方法返回應該返回0。
定製排序

自然排序是根據集合元素的大小,以升序排列,如果要定製排序,應該使用Comparator接口,實現 int compare(T o1,T o2)方法。

package com.test;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;

/**

  • @description 幾個set的比較
  • HashSet:哈希表是通過使用稱爲散列法的機制來存儲信息的,元素並沒有以某種特定順序來存放;
  • LinkedHashSet:以元素插入的順序來維護集合的鏈接表,允許以插入的順序在集合中迭代;
  • TreeSet:提供一個使用樹結構存儲Set接口的實現,對象以升序順序存儲,訪問和遍歷的時間很快。
  • @author Zhou-Jingxian

*/
public class SetDemo {

public static void main(String[] args) {  

    HashSet<String> hs = new HashSet<String>();  
    hs.add("B");  
    hs.add("A");  
    hs.add("D");  
    hs.add("E");  
    hs.add("C");  
    hs.add("F");  
    System.out.println("HashSet 順序:\n"+hs);  

    LinkedHashSet<String> lhs = new LinkedHashSet<String>();  
    lhs.add("B");  
    lhs.add("A");  
    lhs.add("D");  
    lhs.add("E");  
    lhs.add("C");  
    lhs.add("F");  
    System.out.println("LinkedHashSet 順序:\n"+lhs);  

    TreeSet<String> ts = new TreeSet<String>();  
    ts.add("B");  
    ts.add("A");  
    ts.add("D");  
    ts.add("E");  
    ts.add("C");  
    ts.add("F");  
    System.out.println("TreeSet 順序:\n"+ts);  
}  

}
輸出結果:

HashSet 順序:[D, E, F, A, B, C]
LinkedHashSet 順序:[B, A, D, E, C, F]
TreeSet 順序:[A, B, C, D, E, F]
5、Iterator和ListIterator區別

我們在使用List,Set的時候,爲了實現對其數據的遍歷,我們經常使用到了Iterator(迭代器)。使用迭代器,你不需要干涉其遍歷的過程,只需要每次取出一個你想要的數據進行處理就可以了。但是在使用的時候也是有不同的。

List和Set都有iterator()來取得其迭代器。對List來說,你也可以通過listIterator()取得其迭代器,兩種迭代器在有些時候是不能通用的,Iterator和ListIterator主要區別在以下方面:

ListIterator有add()方法,可以向List中添加對象,而Iterator不能
ListIterator和Iterator都有hasNext()和next()方法,可以實現順序向後遍歷,但是ListIterator有hasPrevious()和previous()方法,可以實現逆向(順序向前)遍歷。Iterator就不可以。
ListIterator可以定位當前的索引位置,nextIndex()和previousIndex()可以實現。Iterator沒有此功能。
都可實現刪除對象,但是ListIterator可以實現對象的修改,set()方法可以實現。Iierator僅能遍歷,不能修改。
因爲ListIterator的這些功能,可以實現對LinkedList等List數據結構的操作。其實,數組對象也可以用迭代器來實現。

6、Collection 和 Collections區別

(1)java.util.Collection 是一個集合接口(集合類的一個頂級接口)。它提供了對集合對象進行基本操作的通用接口方法。Collection接口在Java 類庫中有很多具體的實現。Collection接口的意義是爲各種具體的集合提供了最大化的統一操作方式,其直接繼承接口有List與Set。

Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
(2)java.util.Collections 是一個包裝類(工具類/幫助類)。它包含有各種有關集合操作的靜態多態方法。此類不能實例化,就像一個工具類,用於對集合中元素進行排序、搜索以及線程安全等各種操作,服務於Java的Collection框架。

代碼示例:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class TestCollections {

public static void main(String args[]) { 
    //注意List是實現Collection接口的 
    List list = new ArrayList(); 
    double array[] = { 112, 111, 23, 456, 231 }; 
    for (int i = 0; i < array.length; i++) { 
        list.add(new Double(array[i])); 
    } 
    Collections.sort(list); 
    for (int i = 0; i < array.length; i++) { 
        System.out.println(list.get(i)); 
    } 
    // 結果:23.0 111.0 112.0 231.0 456.0 
} 

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章