java集合

java集合 java

  • Java集合框架
  • Collection接口API
  • Iterator迭代器接口(遍歷集合中的元素)
  • Collection子接口之一:Set接口
    • HashSet LinkedHashSet TreeSet
  • Collection子接口之二: List接口
    • ArrayList LinkedList Vector
  • Map接口
    • HashMap TreeMap Hashtable
  • Collections工具類

Java 集合概述

  • 一方面, 面嚮對象語言對事物的體現都是以對象的形式,爲了方便對多個對象的操做,就要對對象進行存儲。另外一方面,使用Array存儲對象方面具備一些弊端,而Java 集合就像一種容器,能夠動態地把多個對象的引用放入容器中。
  • Java 集合類能夠用於存儲數量不等的多個 對象,還可用於保存具備映射關係的關聯數組。
  • 數組的弊端:一旦創建 長度不可變。數組中數據類型必須一致,數組中元素個數未知。
  • Java 集合可分爲 Collection 和 Map 兩種體系
    • Collection接口:
      • Set:元素無序、不可重複的集合 ---相似高中的"集合"
      • List:元素有序,可重複的集合 ---"動態"數組
        • Map接口:具備映射關係"key-value對"的集合 ---相似於高中的"函數"

  • Map接口繼承樹

Collection 接口

  • Collection 接口是 List、Set 和 Queue 接口的父接口,該接口裏定義的方法既可用於操做 Set 集合,也可用於操做 List 和 Queue 集合。
  • JDK不提供此接口的任何直接實現,而是提供更具體的子接口(如:Set和List)實現。
  • 在 Java5 以前,Java 集合會丟失容器中全部對象的數據類型,把全部對象都當成 Object 類型處理;從 Java5 增長了 泛型之後,Java 集合能夠記住容器中對象的數據類型

Collection 接口的方法

紅色部分:集合與數組間轉換操做的方法:以下圖 面試

使用 Iterator 接口遍歷集合元素

  • Iterator對象稱爲迭代器(設計模式的一種),主要用於遍歷 Collection 集合中的元素。
  • 全部實現了 Collection接口的集合類都有一個iterator()方法,用以返回一個實現了Iterator接口的對象。
  • Iterator 僅用於遍歷集合,Iterator 自己並不提供承裝對象的能力。若是須要建立 Iterator 對象,則必須有一個被迭代的集合。

Iterator接口的方法

在調用it.next()方法以前必需要調用it.hasNext()進行檢測。若不調用,且下一條記錄無效,直接調用it.next()會拋出NoSuchElementException異常 算法

 

while(it.hasNext()){ 設計模式

    …..it.next(); 數組

} 安全

使用 foreach 循環遍歷集合元素 數據結構

  •     Java 5 提供了 foreach 循環迭代訪問 Collection

List接口

  • Java中數組用來存儲數據的侷限性
  • List集合類中 元素有序、且可重複,集合中的每一個元素都有其對應的順序索引。
  • List容器中的元素都對應一個整數型的序號記載其在容器中的位置,能夠根據序號存取容器中的元素。
  • JDK API 中List接口的實現類經常使用的有:ArrayList、LinkedList和Vector。
  • List 集合裏添加了一些根據索引來操做集合元素的方法
    • void add(int index, Object ele)
    • boolean addAll(int index, Collection eles)
    • Object get(int index)
    • int indexOf(Object obj)
    • int lastIndexOf(Object obj)
    • Object remove(int index)
    • Object set(int index, Object ele)
    • List subList(int fromIndex, int toIndex)

List實現類之一:ArrayList

  • ArrayList 是 List 接口的典型實現類
  • 本質上,ArrayList是對象引用的一個 變長數組
  • ArrayList 是線程不安全的,而 Vector 是線程安全的,即便爲保證 List 集合線程安全,也不推薦使用Vector
  • Arrays.asList(…) 方法返回的 List 集合既不是 ArrayList 實例,也不是 Vector 實例。 Arrays.asList(…) 返回值是一個固定長度的 List 集合

List實現類之二:LinkedList

  • 對於 頻繁的插入或刪除元素的操做,建議使用LinkedList類,效率較高
  • 新增方法:
    • void addFirst(Object obj)
    • void addLast(Object obj)    
    • Object getFirst()
    • Object getLast()
    • Object removeFirst()
    • Object removeLast()

List 實現類之三:Vector

  • Vector 是一個古老的集合,JDK1.0就有了。大多數操做與ArrayList相同,區別之處在於 Vector是線程安全的。
  • 在各類list中,最好把ArrayList做爲缺省選擇。當插入、刪除頻繁時,使用LinkedList;Vector老是比ArrayList慢,因此儘可能避免使用。
  • 新增方法:
    • void addElement(Object obj)
    • void insertElementAt(Object obj,int index)
    • void setElementAt(Object obj,int index)
    • void removeElement(Object obj)
    • void removeAllElements()

ListIterator接口(瞭解)

  • List 額外提供了一個 listIterator() 方法,該方法返回一個 ListIterator 對象, ListIterator 接口繼承了 Iterator 接口,提供了專門操做 List 的方法:
    • void add()
    • boolean hasPrevious()
    • Object previous()
    • Boolean hasNext()
    • Object next()

Iterator和ListIterator主要區別(瞭解)

一、ListIterator和Iterator都有hasNext()和next()方法,能夠實現順序向後遍歷。可是ListIterator有hasPrevious()和previous()方法,能夠實現逆向(順序向前)遍歷。Iterator就不能夠。 多線程

二、ListIterator能夠定位當前的索引位置,nextIndex()和previousIndex()能夠實現。Iterator 沒有此功能。 併發

三、ListIterator有add()方法,能夠向List中插入對象,而Iterator不能。 框架

四、均可實現刪除對象,可是ListIterator能夠實現對象的修改,set()方法能夠實現。Iterator僅能遍歷,不能修改。由於ListIterator的這些功能,能夠實現對LinkedList等List數據結構的操做。

 

Set 接口

  • Set接口是Collection的子接口,set接口沒有提供額外的方法
  • Set 集合不容許包含相同的元素,若是試把兩個相同的元素加入同一個 Set 集合中,則添加操做失敗。
  • Set 判斷兩個對象是否相同不是使用 == 運算符,而是根據 equals 方法

Set實現類之一:HashSet

  • HashSet 是 Set 接口的典型實現,大多數時候使用 Set 集合時都使用這個實現類。
  • HashSet 按 Hash 算法來存儲集合中的元素,所以具備很好的存取和查找性能。
  • HashSet 具備如下特色:
    • 不能保證元素的排列順序
    • HashSet 不是線程安全的
    • 集合元素能夠是 null
  • 當向 HashSet 集合中存入一個元素時,HashSet 會調用該對象的 hashCode() 方法來獲得該對象的 hashCode 值,而後根據 hashCode 值決定該對象在 HashSet 中的存儲位置。
  • HashSet 集合判斷兩個元素相等的標準:兩個對象經過 hashCode() 方法比較相等,而且兩個對象的 equals() 方法返回值也相等。

hashCode() 方法

  • 若是兩個元素的 equals() 方法返回 true,但它們的 hashCode() 返回值不相等,hashSet 將會把它們存儲在不一樣的位置,但依然能夠添加成功。
  • 對於存放在Set容器中的對象,對應的類必定要重寫equals()和hashCode(Object obj)方法,以實現對象相等規則。
  • 重寫 hashCode() 方法的 基本原則
    • 在程序運行時,同一個對象屢次調用 hashCode() 方法應該返回相同的值
    • 當兩個對象的 equals() 方法比較返回 true 時,這兩個對象的 hashCode() 方法的返回值也應相等

對象中用做 equals() 方法比較的 Field,都應該用來計算 hashCode 值

Set實現類之二:LinkedHashSet

  • LinkedHashSet 是 HashSet 的子類
  • LinkedHashSet 根據元素的 hashCode 值來決定元素的存儲位置,但它同 時使用鏈表維護元素的次序,這使得元素看起來是以插入順序保存的。
  • LinkedHashSet插入性能略低於 HashSet,但在迭代訪問 Set 裏的所有元素時有很好的性能。
  • LinkedHashSet 不容許集合元素重複。

Set實現類之三:TreeSet

  • TreeSet 是 SortedSet 接口的實現類,TreeSet 能夠確保集合元素處於排序狀態。
    • Comparator comparator()
    • Object first()
    • Object last()
    • Object lower(Object e)
    • Object higher(Object e)
    • SortedSet subSet(fromElement, toElement)
    • SortedSet headSet(toElement)
    • SortedSet tailSet(fromElement)
  • TreeSet 兩種排序方法: 天然排序定製排序。默認狀況下 ,TreeSet 採用天然排序.

     

排 序——天然排序 Comparable 接口

  • 天然排序:TreeSet 會調用集合元素的 compareTo(Object obj) 方法來比較元素之間的大小關係,而後將集合元素按升序排列
  • 若是試圖把一個對象添加到 TreeSet 時,則該對象的類必須實現 Comparable 接口。
  • 實現 Comparable 的類必須實現 compareTo(Object obj) 方法,兩個對象即經過 compareTo(Object obj) 方法的返回值來比較大小。
  • Comparable 的典型實現
    • BigDecimal、BigInteger 以及全部的數值型對應的包裝類:按它們對應的數值大小進行比較
    • Character:按字符的 unicode值來進行比較
    • Boolean:true 對應的包裝類實例大於 false 對應的包裝類實例
    • String:按字符串中字符的 unicode 值進行比較
    • Date、Time:後邊的時間、日期比前面的時間、日期大
  • 向 TreeSet 中添加元素時,只有第一個元素無須比較compareTo()方法,後面添加的全部元素都會調用compareTo()方法進行比較。
  • 由於只有相同類的兩個實例纔會比較大小,因此向 TreeSet 中添加的應該是同一個類的對象
  • 對於 TreeSet 集合而言,它 判斷兩個對象是否相等的惟一標準是:兩個對象經過 compareTo(Object obj) 方法比較返回值
  • 當須要把一個對象放入 TreeSet 中,重寫該對象對應的 equals() 方法時,應保證該方法與 compareTo(Object obj) 方法有一致的結果 :若是兩個對象經過 equals() 方法比較返回 true,則經過 compareTo(Object obj) 方法比較應返回 0

 

排 序——定製排序 comparator接口

  • TreeSet的天然排序是根據集合元素的大小,進行元素升序排列。若是須要定製排序,好比降序排列,可經過Comparator接口的幫助。須要重寫compare(T o1,T o2)方法。
  • 利用int compare(T o1,T o2)方法,比較o1和o2的大小:若是方法返回正整數,則表示o1大於o2;若是返回0,表示相等;返回負整數,表示o1小於o2。
  • 要實現定製排序,須要將實現Comparator接口的實例做爲形參傳遞給TreeSet的構造器。
  • 此時,仍然只能向TreeSet中添加類型相同的對象。不然發生ClassCastException異常。
  • 使用定製排序判斷兩個元素相等的標準是:經過Comparator比較兩個元素返回了0。

Map接口

  • Map與Collection並列存在。用於保存具備 映射關係的數據:Key-Value
  • Map 中的 key 和 value 均可以是任何引用類型的數據
  • Map 中的 key 用Set來存放, 不容許重複,即同一個 Map 對象所對應的類,須重寫hashCode()和equals()方法。
  • 經常使用String類做爲Map的"鍵"。
  • key 和 value 之間存在單向一對一關係,即經過指定的 key 總能找到惟一的、肯定的 value。

Map實現類之一:HashMap

  • Map接口的經常使用實現類: HashMap、TreeMap和Properties。
  • HashMap是 Map 接口 使用頻率最高的實現類。
  • 容許使用null鍵和null值,與HashSet同樣,不保證映射的順序。
  • HashMap 判斷兩個 key 相等的標準是:兩個 key 經過 equals() 方法返回 true,hashCode 值也相等。
  • HashMap 判斷兩個 value相等的標準是:兩個 value 經過 equals() 方法返回 true。

Map實現類之二:LinkedHashMap

  • LinkedHashMap 是 HashMap 的子類
  • 與LinkedHashSet相似,LinkedHashMap 能夠維護 Map 的迭代順序:迭代順序與 Key-Value 對的插入順序一致

Map實現類之三:TreeMap

  • TreeMap存儲 Key-Value 對時,須要根據 key-value 對進行排序。TreeMap 能夠保證全部的 Key-Value 對處於有序狀態。
  • TreeMap 的 Key 的排序:
    • 天然排序:TreeMap 的全部的 Key 必須實現 Comparable 接口,並且全部的 Key 應該是同一個類的對象,不然將會拋出 ClasssCastException
    • 定製排序:建立 TreeMap 時,傳入一個 Comparator 對象,該對象負責對 TreeMap 中的全部 key 進行排序。此時不須要 Map 的 Key 實現 Comparable 接口
  • TreeMap判斷 兩個key相等的標準:兩個key經過compareTo()方法或者compare()方法返回0。

若使用自定義類做爲TreeMap的key,所屬類須要重寫equals()和hashCode()方法,且equals()方法返回true時,compareTo()方法應返回0

Map實現類之四:Hashtable

  • Hashtable是個古老的 Map 實現類,線程安全。
  • 與HashMap不一樣,Hashtable 不容許使用 null 做爲 key 和 value
  • 與HashMap同樣,Hashtable 也不能保證其中 Key-Value 對的順序
  • Hashtable判斷兩個key相等、兩個value相等的標準,與hashMap一致。

Map實現類之五:Properties

  • Properties 類是 Hashtable 的子類,該對象用於處理屬性文件
  • 因爲屬性文件裏的 key、value 都是字符串類型, 因此 Properties 裏的 key 和 value 都是字符串類型
  • 存取數據時,建議使用setProperty(String key,String value)方法和getProperty(String key)方法

Properties pros = new Properties();

pros.load(new FileInputStream("jdbc.properties"));

String user = pros.getProperty("user");

System.out.println(user);

操做集合的工具類:Collections

  • Collections 是一個操做 Set、List 和 Map 等集合的工具類
  • Collections 中提供了一系列靜態的方法對集合元素進行排序、查詢和修改等操做,還提供了對集合對象設置不可變、對集合對象實現同步控制等方法
  • 排序操做(均爲static方法)
    • reverse(List):反轉 List 中元素的順序
    • shuffle(List):對 List 集合元素進行隨機排序
    • sort(List):根據元素的天然順序對指定 List 集合元素按升序排序
    • sort(List,Comparator):根據指定的 Comparator 產生的順序對 List 集合元素進行排序
    • swap(List,int, int):將指定 list 集合中的 i 處元素和 j 處元素進行交換

查找、替換

  •     Object max(Collection):根據元素的天然順序,返回給定集合中的最大元素
  • Object max(Collection,Comparator):根據 Comparator 指定的順序,返回給定集合中的最大元素
  • Object min(Collection)
  • Object min(Collection,Comparator)
  • int frequency(Collection,Object):返回指定集合中指定元素的出現次數
  • void copy(List dest,List src):將src中的內容複製到dest中
  • boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替換 List 對象的全部舊值

 

同步控制

  • Collections 類中提供了多個 synchronizedXxx() 方法,該方法可以使將指定集合包裝成線程同步的集合,從而能夠解決多線程併發訪問集合時的線程安全問題

Enumeration(瞭解)

  • Enumeration 接口是 Iterator 迭代器的 "古老版本"

Enumeration stringEnum = new StringTokenizer("a-b*c-d-e-g", "-");

    while(stringEnum.hasMoreElements()){

        Object obj = stringEnum.nextElement();

        System.out.println(obj);

    }

 

集合部分面試題

1 Collection 和 Collections的區別

答:Collection是集合類的上級接口,繼承與他的接口主要有Set 和List.

Collections是針對集合類的一個幫助類,他提供一系列靜態方法實現對各類集合的搜索、排序、線程安全化等操做

 

--------------------------------------------------

2 Set裏的元素是不能重複的,那麼用什麼方法來區分重複與否呢? 是用==仍是equals()? 它們有何區別

答:Set裏的元素是不能重複的,那麼用iterator()方法來區分重複與否。equals()是判讀兩個Set是否相等

equals()和==方法決定引用值是否指向同一對象equals()在類中被覆蓋,爲的是當兩個分離的對象的內容

和類型相配的話,返回真值

 

--------------------------------------------------

3 List, Set, Map是否繼承自Collection接口

答: List,Set是,Map不是

 

--------------------------------------------------

4 兩個對象值相同(x.equals(y) == true),但卻可有不一樣的hash code,這句話對不對

答:不對,有相同的hash code

 

--------------------------------------------------

5 說出ArrayList,Vector, LinkedList的存儲性能和特性

答:ArrayList和Vector都是使用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增長和插入元素,它們都容許直接按序號索引元素,可是插入元素要涉及數組元素移動等內存操做,因此索引數據快而插入數據慢,Vector因爲使用了synchronized方法(線程安全),一般性能上較ArrayList差,而LinkedList使用雙向鏈表實現存儲,按序號索引數據須要進行前向或後向遍歷,可是插入數據時只須要記錄本項的先後項便可,因此插入速度較快。

 

--------------------------------------------------

6 HashMap和Hashtable的區別

答:HashMap是Hashtable的輕量級實現(非線程安全的實現),他們都完成了Map接口,主要區別在於

HashMap容許空(null)鍵值(key),因爲非線程安全,效率上可能高於Hashtable。

HashMap容許將null做爲一個entry的key或者value,而Hashtable不容許。

HashMap把Hashtable的contains方法去掉了,改爲containsvalue和containsKey。由於contains方法容易讓人引發誤解。

Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map interface的一個實現。

最大的不一樣是,Hashtable的方法是Synchronize的,而HashMap不是,在多個線程訪問Hashtable時,不須要本身爲它的方法實現同步,而HashMap 就必須爲之提供外同步。

Hashtable和HashMap採用的hash/rehash算法都大概同樣,因此性能不會有很大的差別。

 

--------------------------------------------------

 

7 ArrayList和Vector的區別,HashMap和Hashtable的區別

答:就ArrayList與Vector主要從二方面來講.

一.同步性:Vector是線程安全的,也就是說是同步的,而ArrayList是線程序不安全的,不是同步的

二.數據增加:當須要增加時,Vector默認增加爲原來一培,而ArrayList倒是原來的一半

就HashMap與HashTable主要從三方面來講。

一.歷史緣由:Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現

二.同步性:Hashtable是線程安全的,也就是說是同步的,而HashMap是線程序不安全的,不是同步的

三.值:只有HashMap可讓你將空值做爲一個表的條目的key或value

 

 

 

8 如何高效地判斷數組中是否包含某特定值

如何檢查一個未排序的數組中是否包含某個特定值,這是一個在Java中很是實用而且頻繁使用的操做。檢查數組中是否包含特定值能夠用多種不一樣的方式實現,可是時間複雜度差異很大。下面,將爲你們展現各類方法及其須要花費的時間。

1.檢查數組中是否包含特定值的四種不一樣方法

1)使用List:

public static boolean useList(String[] arr, String targetValue) {

    return Arrays.asList(arr).contains(targetValue);

}

2)使用Set

1

2

3

4

public static boolean useSet(String[] arr, String targetValue) {

    Set<String> set = new HashSet<String>(Arrays.asList(arr));

    return set.contains(targetValue);

}

3)使用一個簡單循環:

1

2

3

4

5

6

7

public static boolean useLoop(String[] arr, String targetValue) {

    for(String s: arr){

        if(s.equals(targetValue))

            return true;

    }

    return false;

}

4)使用Arrays.binarySearch()

注:下面的代碼是錯誤的,這樣寫出來僅僅爲了理解方便binarySearch()只能用於已排好序的數組中。因此,你會發現下面結果很奇怪。

1

2

3

4

5

6

7

public static boolean useArraysBinarySearch(String[] arr, String targetValue) {

    int a =  Arrays.binarySearch(arr, targetValue);

    if(a > 0)

        return true;

    else

        return false;

}

2.時間複雜度

經過下面的這段代碼能夠近似比較幾個方法的時間複雜度。雖然分別搜索一個大小爲51K10K的數組是不夠精確的,可是思路是清晰的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

public static void main(String[] args) {

    String[] arr = new String[] {  "CD",  "BC", "EF", "DE", "AB"};

    

    //use list

    long startTime = System.nanoTime();

    for (int i = 0; i < 100000; i++) {

        useList(arr, "A");

    }

    long endTime = System.nanoTime();

    long duration = endTime - startTime;

    System.out.println("useList:  " + duration / 1000000);

    

    //use set

    startTime = System.nanoTime();

    for (int i = 0; i < 100000; i++) {

        useSet(arr, "A");

    }

    endTime = System.nanoTime();

    duration = endTime - startTime;

    System.out.println("useSet:  " + duration / 1000000);

    

    //use loop

    startTime = System.nanoTime();

    for (int i = 0; i < 100000; i++) {

        useLoop(arr, "A");

    }

    endTime = System.nanoTime();

    duration = endTime - startTime;

    System.out.println("useLoop:  " + duration / 1000000);

    

    //use Arrays.binarySearch()

    startTime = System.nanoTime();

    for (int i = 0; i < 100000; i++) {

        useArraysBinarySearch(arr, "A");

    }

    endTime = System.nanoTime();

    duration = endTime - startTime;

    System.out.println("useArrayBinary:  " + duration / 1000000);

}

結果:

1

2

3

4

useList:  13

useSet:  72

useLoop:  5

useArraysBinarySearch:  9

對於長度爲1K的數組:

1

2

3

4

5

6

String[] arr = new String[1000];

    

Random s = new Random();

for(int i=0; i< 1000; i++){

    arr[i] = String.valueOf(s.nextInt());

}

結果:

1

2

3

4

useList:  112

useSet:  2055

useLoop:  99

useArrayBinary:  12

對於長度爲10K的數組:

1

2

3

4

5

6

String[] arr = new String[10000];

    

Random s = new Random();

for(int i=0; i< 10000; i++){

    arr[i] = String.valueOf(s.nextInt());

}

結果:

1

2

3

4

useList:  1590

useSet:  23819

useLoop:  1526

useArrayBinary:  12

很明顯,使用簡單循環的方法比使用其餘任何集合效率更高。許多開發者會使用第一種方法,可是它並非高效的。將數組壓入Collection類型中,須要首先將數組元素遍歷一遍,而後再使用集合類作其餘操做。

若是使用Arrays.binarySearch()方法,數組必須是已排序的。因爲上面的數組並無進行排序,因此該方法不可以使用。

實際上,若是你須要藉助數組或者集合類高效地檢查數組中是否包含特定值,一個已排序的列表或樹能夠作到時間複雜度爲O(log(n))hashset能夠達到O(1)

相關文章
相關標籤/搜索