程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> Java Map 集合類簡介

Java Map 集合類簡介

編輯:JAVA編程入門知識

  Java.util 中的集合類包含 Java 中某些最常用的類。 最常用的集合類是 List 和 Map。 List 的具體實現包括 ArrayList 和 Vector,它們是可變大小的列表,比較適合構建、存儲和操作任何類型對象的元素列表。 List 適用於按數值索引訪問元素的情形。
  
  Map 提供了一個更通用的元素存儲方法。 Map 集合類用於存儲元素對(稱作“鍵”和“值”),其中每個鍵映射到一個值。 從概念上而言,您可以將 List 看作是具有數值鍵的 Map。 而實際上,除了 List 和 Map 都在定義 java.util 中外,兩者並沒有直接的聯系。本文將著重介紹核心 Java 發行套件中附帶的 Map,同時還將介紹如何采用或實現更適用於您應用程序特定數據的專用 Map。
  
  了解 Map 接口和方法
  
  Java 核心類中有很多預定義的 Map 類。 在介紹具體實現之前,我們先介紹一下 Map 接口本身,以便了解所有實現的共同點。 Map 接口定義了四種類型的方法,每個 Map 都包含這些方法。 下面,我們從兩個普通的方法(表 1)開始對這些方法加以介紹。
  
  表 1: 覆蓋的方法。 我們將這 Object 的這兩個方法覆蓋,以正確比較 Map 對象的等價性。 equals(Object o) 比較指定對象與此 Map 的等價性
  hashCode() 返回此 Map 的哈希碼
  
  
  
  Map 構建
  
  Map 定義了幾個用於插入和刪除元素的變換方法(表 2)。
  
  表 2: Map 更新方法: 可以更改 Map 內容。 clear() 從 Map 中刪除所有映射
  remove(Object key) 從 Map 中刪除鍵和關聯的值
  put(Object key, Object value) 將指定值與指定鍵相關聯
  clear() 從 Map 中刪除所有映射
  putAll(Map t) 將指定 Map 中的所有映射復制到此 map
  
  
  
  盡管您可能注重到,縱然假設忽略構建一個需要傳遞給 putAll() 的 Map 的開銷,使用 putAll() 通常也並不比使用大量的 put() 調用更有效率,但 putAll() 的存在一點也不稀奇。 這是因為,putAll() 除了迭代 put() 所執行的將每個鍵值對添加到 Map 的算法以外,還需要迭代所傳遞的 Map 的元素。 但應注重,putAll() 在添加所有元素之前可以正確調整 Map 的大小,因此假如您未親自調整 Map 的大小(我們將對此進行簡單介紹),則 putAll() 可能比預期的更有效。
  
  查看 Map
  
  迭代 Map 中的元素不存在直接了當的方法。 假如要查詢某個 Map 以了解其哪些元素滿足特定查詢,或假如要迭代其所有元素(無論原因如何),則您首先需要獲取該 Map 的“視圖”。 有三種可能的視圖(參見表 3)
  
  所有鍵值對 — 參見 entrySet()
  所有鍵 — 參見 keySet()
  所有值 — 參見 values()
  
  前兩個視圖均返回 Set 對象,第三個視圖返回 Collection 對象。 就這兩種情況而言,問題到這裡並沒有結束,這是因為您無法直接迭代 Collection 對象或 Set 對象。要進行迭代,您必須獲得一個 Iterator 對象。 因此,要迭代 Map 的元素,必須進行比較煩瑣的編碼
  
  
  Iterator keyValuePairs = aMap.entrySet().iterator();
  Iterator keys = aMap.keySet().iterator();
  Iterator values = aMap.values().iterator();
  
  
  值得注重的是,這些對象(Set、Collection 和 Iterator)實際上是基礎 Map 的視圖,而不是包含所有元素的副本。 這使它們的使用效率很高。 另一方面,Collection 或 Set 對象的 toArray() 方法卻創建包含 Map 所有元素的數組對象,因此除了確實需要使用數組中元素的情形外,其效率並不高。
  
  我運行了一個小測試(隨附文件中的 Test1),該測試使用了 HashMap,並使用以下兩種方法對迭代 Map 元素的開銷進行了比較:
  
  
  int mapsize = aMap.size();
  
  Iterator keyValuePairs1 = aMap.entrySet().iterator();
  for (int i = 0; i < mapsize; i++)
  {
  Map.Entry entry = (Map.Entry) keyValuePairs1.next();
  Object key = entry.getKey();
  Object value = entry.getValue();
  ...
  }
  
  Object[] keyValuePairs2 = aMap.entrySet().toArray();
  for (int i = 0; i < rem; i++) {
  {
  Map.Entry entry = (Map.Entry) keyValuePairs2[i];
  Object key = entry.getKey();
  
  
  Object value = entry.getValue();
  ...
  }
  
  
  此測試使用了兩種測量方法: 一種是測量迭代元素的時間,另一種測量使用 toArray 調用創建數組的其他開銷。 第一種方法(忽略創建數組所需的時間)表明,使用已從 toArray 調用中創建的數組迭代元素的速度要比使用 Iterator 的速度大約快 30%-60%。 但假如將使用 toArray 方法創建數組的開銷包含在內,則使用 Iterator 實際上要快 10%-20%。 因此,假如由於某種原因要創建一個集合元素的數組而非迭代這些元素,則應使用該數組迭代元素。 但假如您不需要此中間數組,則不要創建它,而是使用 Iterator 迭代元素。
  
  表 3: 返回視圖的 Map 方法: 使用這些方法返回的對象,您可以遍歷 Map 的元素,還可以刪除 Map 中的元素。 entrySet() 返回 Map 中所包含映射的 Set 視圖。 Set 中的每個元素都是一個 Map.Entry 對象,可以使用 getKey() 和 getValue() 方法(還有一個 setValue() 方法)訪問後者的鍵元素和值元素
  
   keySet() 返回 Map 中所包含鍵的 Set 視圖。 刪除 Set 中的元素還將刪除 Map 中相應的映射(鍵和值)
  values() 返回 map 中所包含值的 Collection 視圖。 刪除 Collection 中的元素還將刪除 Map 中相應的映射(鍵和值)
  
  
  
  訪問元素
  
  表 4 中列出了 Map 訪問方法。Map 通常適合按鍵(而非按值)進行訪問。 Map 定義中沒有規定這肯定是真的,但通常您可以期望這是真的。 例如,您可以期望 containsKey() 方法與 get() 方法一樣快。 另一方面,containsValue() 方法很可能需要掃描 Map 中的值,因此它的速度可能比較慢。
  
  表 4: Map 訪問和測試方法: 這些方法檢索有關 Map 內容的信息但不更改 Map 內容。 get(Object key) 返回與指定鍵關聯的值
  containsKey(Object key) 假如 Map 包含指定鍵的映射,則返回 true
  containsValue(Object value) 假如此 Map 將一個或多個鍵映射到指定值,則返回 true
  isEmpty() 假如 Map 不包含鍵-值映射,則返回 true
  size() 返回 Map 中的鍵-值映射的數目
  
  
  
  對使用 containsKey() 和 containsValue() 遍歷 HashMap 中所有元素所需時間的測試表明,containsValue() 所需的時間要長很多。 實際上要長幾個數量級! (參見圖 1 和圖 2,以及隨附文件中的 Test2)。 因此,假如 containsValue() 是應用程序中的性能問題,它將很快顯現出來,並可以通過監測您的應用程序輕松地將其識別。 這種情況下,我相信您能夠想出一個有效的替換方法來實現 containsValue() 提供的等效功能。 但假如想不出辦法,則一個可行的解決方案是再創建一個 Map,並將第一個 Map 的所有值作為鍵。 這樣,第一個 Map 上的 containsValue() 將成為第二個 Map 上更有效的 containsKey()。
  
  
  圖 1: 使用 JDeveloper 創建並運行 Map 測試類
  
  
  
  
  圖 2: 在 JDeveloper 中使用執行監測器進行的性能監測查出應用程序中的瓶頸
  
  
  
  核心 Map
  
  Java 自帶了各種 Map 類。 這些 Map 類可歸為三種類型:
  
  
  通用 Map,用於在應用程序中治理映射,通常在 java.util 程序包中實現
  HashMap
  Hashtable
  Properties
  LinkedHashMap
  IdentityHashMap
  TreeMap
  WeakHashMap
  ConcurrentHashMap
  專用 Map,您通常不必親自創建此類 Map,而是通過某些其他類對其進行訪問
  java.util.jar.Attributes
  javax.print.attribute.standard.PrinterStateReasons
  java.security.Provider
  java.awt.RenderingHints
  javax.swing.UIDefaults
  一個用於幫助實現您自己的 Map 類的抽象類
  AbstractMap
  
  內部哈希: 哈希映射技術
  
  幾乎所有通用 Map 都使用哈希映射。 這是一種將元素映射到數組的非常簡單的機制,您應了解哈希映射的工作原理,以便充分利用 Map。
  
  哈希映射結構由一個存儲元素的內部數組組成。 由於內部采用數組存儲,因此必然存在一個用於確定任意鍵訪問數組的索引機制。 實際上,該機制需要提供一個小於數組大小的整數索引值。 該機制稱作哈希函數。 在 Java 基於哈希的 Map 中,哈希函數將對象轉換為一個適合內部數組的整數。 您不必為尋找一個易於使用的哈希函數而大傷腦筋: 每個對象都包含一個返回整數值的 hashCode() 方法。 要將該值映射到數組,只需將其轉換為一個正值,然後在將該值除以數組大小後取余數即可。 以下是一個簡單的、適用於任何對象的 Java 哈希函數
  
  
  int hashvalue = Maths.abs(key.hashCode()) % table.length;
  
  
  (% 二進制運算符(稱作模)將左側的值除以右側的值,然後返回整數形式的余數。)
  
  實際上,在 1.4 版發布之前,這就是各種基於哈希的 Map 類所使用的哈希函數。 但假如您查看一下代碼,您將看到
  
  
  int hashvalue = (key.hashCode() & 0x7FFFFFFF) % table.length;
  
  
  它實際上是使用更快機制獲取正值的同一函數。 在 1.4 版中,HashMap 類實現使用一個不同且更復雜的哈希函數,該函數基於
 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved