天天看點

【Java基礎】集合總結(三)——Queue集合、Map集合

一、Queue集合

【Java基礎】集合總結(三)——Queue集合、Map集合

Queue用于模拟隊列這種資料結構,隊列的特點是“先進先出”(FIFO)。隊列的頭部儲存在隊列中存放時間最長的元素,隊列的尾部儲存在隊列中存放時間最短的元素。新元素插入(offer)到隊列的尾部,通路元素(poll)操作會傳回隊列頭部的元素。通常,隊列不允許随機通路隊列中的元素。

(一)PriorityQueue實作類

PriorityQueue儲存隊列元素的順序并不是按加入隊列的順序,而是按隊列元素的大小進行排序。是以當調用peek()方法或者poll()方法取出隊列時,并不是取出最先進入隊列的元素,而是取出隊列中最小的元素。

//定義一個PriorityQueue類型的隊列,并依次加入6,-3,20
PriorityQueue pq = new PriorityQueue();
pq.offer(6);
pq.offer(-3);
pq.offer(20);

//通路隊列中的第一個元素,輸出最小的元素-3
System.out.println(a.poll());
           

PriorityQueue不允許插入null元素,它還需要對隊列元素進行排序,有兩種排序方式

  • 自然排序:采用自然排序的PriorityQueue集合中的元素必須實作了Comparable接口,而且應該是同一個類的多個執行個體,否則可能導緻ClassCastException異常。
  • 定制排序:建立PriorityQueue隊列時,傳入一個Comparator對象,該對象負責隊列中的所有元素進行排序。采用定制排序時不要求隊列元素實作Comparable接口。

(二)Deque接口與ArrayDeque實作類

Deque代表一個雙端隊列,特點是允許從兩端來操作隊列的元素。Deque不僅可以當成雙端隊列使用,而且可以被當成棧來使用,因為該類裡還包含了pop(出棧)、push(入棧)兩個方法。

Deque接口提供了一個典型的實作類,ArrayDeque。它是一個基于數組實作的雙端隊列,其可以作為棧來使用,也可以作為隊列來使用。

//作為棧使用,先進後出
public class ArrayDequeStack{
   public static void main(String[] args)
   {
      ArrayDeque stack=new ArrayDeque();
      //将三個元素push入“棧”
      stack.push("綠色");
      stack.push("紅色");
      stack.push("黃色");
      System.out.println(stack);
      //通路第一個元素,但并不将其“pop”出棧。輸出:黃色
      System.out.println(stack.peek());
      //pop出第一個元素,輸出:黃色
      System.out.println(stack.pop());
   }
}


//作為隊列使用,先進先出

public class ArrayDequeQueue{
   public static void main(String[] args)
   {
      ArrayDeque queue=new ArrayDeque();
      //将三個元素加入隊列
      queue.offer("綠色");
      queue.offer("紅色");
      queue.offer("黃色");
      System.out.println(queue);
      //通路隊列頭部的元素,但并不将其poll出隊列。輸出:綠色
      System.out.println(queue.peek());
      //poll出第一個元素,輸出:綠色
      System.out.println(stack.poll());
   }
}
           

(三)LinkedList實作類

LinkedList不光實作了List接口,還實作了Queue接口。這意味着它既是一個List集合,可以根據索引來随機通路集合中的元素。也是一個Queue集合,可以被當成雙端隊列來使用。是以既可以當成棧來使用,也可以當成隊列來使用。

public class LinkedListTest{
    public static void main(String[] args)
    {
        LinkedList books = new LinkedList();
        //将字元串元素加入隊列的尾部
        books.offer("A書");
        //将一個字元串元素加入棧的頂部
        books.push("B書");
        //将字元串元素添加到隊列的頭部(相當于棧的頂部)
        books.offerFirst("C書");
        //通路并不删除棧頂的元素
        System.out.println(books.peekFirst());
        //通路并不删除隊列的最後一個元素
        System.out.println(books.peekLast());
        //将棧頂的元素彈出“棧”
        System.out.println(books.pop());
        //通路并删除隊列的最後一個元素
        System.out.println(books.poolLast());
    }
}
           

(四)各種線性表的性能分析

LinkedList與ArrayList、ArrayDeque的實作機制完全不同,ArrayList、ArrayDeque内部以數組的形式來儲存集合中的元素,是以随機通路集合元素時有較好的性能;而LinkedList内部以連結清單的形式來儲存集合中的元素,是以随機通路集合元素時性能較差,但在插入、删除元素時性能比較出色(隻需改變指針所指的位址即可)。總體來說,ArrayList的性能比LinkedList的性能要好,是以大部分的時候都應該考慮使用ArrayList。需要指出的是,雖然Vector也是以數組的形式來存儲集合元素的,但因為它實作了線程同步功能(而且實作機制也不好),是以各方面性能都比較差。

關于使用List集合有如下建議:

  • 如果需要周遊List集合元素,對于ArrayList、Vector集合,應該使用随機通路方法(get)來周遊集合元素,這樣性能更好;對于LinkedList集合,則應該采用疊代器(Iterator)來周遊集合元素。
  • 如果需要經常執行插入、删除操作來改變包含大量資料的List集合的大小,可考慮使用LinkedList集合。使用ArrayList、Vector集合可能需要經常重新配置設定内部數組的大小,效果可能較差。
  • 如果有多個線程需要同時通路List集合中的元素,開發者可考慮使用Collections将集合包裝成線程安全的集合。

二、Map集合

【Java基礎】集合總結(三)——Queue集合、Map集合

Map用于儲存具有映射關系的資料,是key-value類型的。key和value都可以是任何引用類型的資料。key和value之間存在單向一對一關系,即通過指定的key總能找到唯一的、确定的value。Map的key不允許重複。

(一)Map接口常用方法

Map map=new HashMap();
//放入3組資料
map.put("國文",35);
map.put("數學",97);
map.put("英語",43);
//多次放入的key-value對中value可以重複
map.put("國文",95);
//放入重複的key時,新的value會覆寫原有的value
//如果新的value覆寫了原有的value,該方法傳回被覆寫的value
system.out.println(map.put("英語",86)); //輸出43
//判斷是否包含指定的key
map.containsKey("國文");
//判斷是否包含指定的value
map.containsValue(97);
//對map集合進行周遊
for(Object key:map.keySet())
{
    System.out.println(key+"-->"+map.get(key));
}
//根據key來删除key-value對,傳回被删除key所關聯的value,如果該key不存在,則傳回null
map.remove("國文");
           

(二)HashMap實作類

1、HashMap是Map 接口使用頻率最高的實作類,是線程不安全的。

2、允許使用null鍵和null值,與HashSet一樣,不保證映射的順序。

3、所有的key構成的集合是Set:無序的、不可重複的。是以,key所在的類要重寫:equals()和hashCode()。

     所有的value構成的集合是Collection:無序的、可以重複的。是以,value所在的類要重寫: equals()。

4、一個key-value構成一個entry,所有的entry構成的集合是Set:無序的、不可重複的。

5、HashMap 判斷兩個 key 相等的标準是:兩個 key 通過 equals() 方法傳回 true,hashCode 值也相等。

     HashMap 判斷兩個 value 相等的标準是:兩個 value 通過 equals() 方法傳回 true。

6、HashMap的存儲結構:

  • JDK7及以前版本:HashMap是數組+連結清單結構。
  • JDK8版本釋出以後:HashMap是數組+連結清單+紅黑樹實作。

(三)LinkedHashMap實作類

1、在HashMap存儲結構的基礎上,使用了一對雙向連結清單來記錄添加元素的順序。

2、與LinkedHashSet類似, LinkedHashMap 可以維護 Map 的疊代順序:疊代順序與 key-Value 對的插入順序一緻。

3、LinkedHashMap需要維護元素的插入順序,是以性能略低于HashMap的性能;但因為它以連結清單來維護内部順序,是以在疊代通路Map裡的全部元素時将有較好的性能。

4、LinkedHashMap的底層實作原理:

     LinkedHashMap底層使用的結構與HashMap相同,因為LinkedHashMap繼承于HashMap。

     差別就在于:LinkedHashMap内部提供了Entry,替換HashMap中的Node。

(四)Hashtable實作類

1、Hashtable是個古老的 Map 實作類, JDK1.0就提供了。不同于HashMap,Hashtable是線程安全的。

2、Hashtable實作原理和HashMap相同,功能相同。底層都使用哈希表結構,查詢速度快,很多情況下可以互用。

3、與HashMap不同, Hashtable 不允許使用 null 作為 key 和 value。

4、與HashMap一樣, Hashtable 也不能保證其中 Key-Value 對的順序。

5、Hashtable判斷兩個key相等、兩個value相等的标準, 與HashMap一緻。

6、盡量少用Hashtable實作類,即使需要建立線程安全的Map實作類,也無需使用Hashtable實作類,可以通過Collections工具類把HashMap變成線程安全的。

(五)Properties讀寫屬性檔案

1、Properties 類是 Hashtable 的子類,該對象用于處理屬性檔案。

2、由于屬性檔案裡的 key、 value 都是字元串類型,是以 Properties 裡的 key和 value 都是字元串類型。

3、存取資料時,建議使用setProperty(String key,String value)方法設定屬性值和getProperty(String key)方法擷取指定屬性名對應的屬性值。

Properties pros = new Properties();
pros.setProperty("username","Lucy");
String user = pros.getProperty("username");
           

(六)TreeMap實作類

1、TreeMap底層使用紅黑樹結構存儲資料,每個key-value對即作為一個紅黑樹的一個節點。

2、TreeMap存儲 Key-Value 對(節點)時, 需要根據 key對節點進行排序。TreeMap 可以保證所有的 Key-Value 對處于有序狀态。

3、TreeMap 的 Key 的排序:

  • 自然排序: TreeMap 的所有的 Key 必須實作 Comparable 接口,而且所有的 Key 應該是同一個類的對象,否則将會抛出 ClasssCastException異常。
  • 定制排序:建立 TreeMap 時,傳入一個 Comparator 對象,該對象負責對TreeMap 中的所有 key 進行排序。此時不要求 Map 的 Key 實作Comparable 接口。

4、TreeMap判斷兩個key相等的标準是:兩個key通過compareTo()方法或者compare()方法傳回0。

(七) WeakHashMap實作類

WeakHashMap和HashMap的用法基本相似,不同之處在于,HashMap的key保留了對實際對象的強引用,這意味着,隻要該HashMap對象不被銷毀,該HashMap的所有key所引用的對象就不會被垃圾回收,HashMap也不會自動删除對應的key-value對。但WeakHashMap的key隻保留了對實際對象的弱引用,這意味着如果WeakHashMap對象的key所引用的對象沒有被其它強引用變量所引用,則這些key所引用的對象可能被垃圾回收,WeakHashMap也可能自動删除這些key所對應的key-value對。

(八)IdetityHashMap實作類

其實作機制和HashMap基本相似,但它在處理兩個key相等時比較獨特:在IdetityHashMap中,當且僅當兩個key嚴格相等(key1==key2)時,IdentityHashMap才會認為兩個key相等;對于普通的HashMap而言,隻要key1和key2通過equals()方法比較傳回true,且它們的hashCode值相等即可。

(九)EnumMap實作類

EnumMap是和枚舉類一起使用的Map實作,EnumMap中的所有key都必須是單個枚舉類的枚舉值。建立EnumMap時必須顯式或隐式指定它對應的枚舉類。EnumMap内部以數組的形式儲存,根據key的自然順序(即枚舉值在枚舉類中的定義順序)來維護key-value對的順序,不允許使用null作為key值,但可以作為value值。

(十)各Map實作類的性能分析

由于Hashtable是一個古老的、線程安全的集合,是以HashMap通常比Hashtable要快。

TreeMap通常比HashMap、Hashtable要慢(尤其在插入、删除key-value對時更慢),因為TreeMap底層采用紅黑樹來管理key-value對。TreeMap中的key-value對總是處于有序狀态,無須專門進行排序操作。

對于一般的使用場景,程式應該多考慮使用HashMap,因為HashMap正是為快速查詢設計的。但如果程式需要一個總是排好序的Map時,則可以考慮使用TreeMap。

LinkedHashMap比HashMap慢一點,因為它需要維護連結清單來保持Map中key-value時的添加順序。IdentityHashMap性能沒有特别出色之處,因為它采用與HashMap基本相似的實作,隻是塔使用==而不是equals()方法來判斷元素相等。EnumMap的性能最好,但它隻能使用同一個枚舉類的枚舉值作為key。