Java的TreeMap和HashMap的介紹和使用
1. TreeMap的介紹和使用
第1部分 TreeMap介紹
TreeMap 簡介
TreeMap 是一個有序的key-value集合,它是通過紅黑樹實現的。
TreeMap 繼承於AbstractMap,所以它是一個Map,即一個key-value集合。
TreeMap 實現了NavigableMap介面,意味著它支援一系列的導航方法。比如返回有序的key集合。
TreeMap 實現了Cloneable介面,意味著它能被克隆。
TreeMap 實現了java.io.Serializable介面,意味著它支援序列化。
TreeMap基於紅黑樹(Red-Black tree)實現。該對映根據其鍵的自然順序進行排序
TreeMap的基本操作 containsKey、get、put 和 remove 的時間複雜度是 log(n) 。
另外,TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。
// 預設建構函式。使用該建構函式,TreeMap中的元素按照自然排序進行排列。 TreeMap() // 建立的TreeMap包含Map TreeMap(Map<? extends K, ? extends V> copyFrom) // 指定Tree的比較器 TreeMap(Comparator<? super K> comparator) // 建立的TreeSet包含copyFrom TreeMap(SortedMap<K, ? extends V> copyFrom)
TreeMap的API
Map.Entry<K,V> ceilingEntry(K key) 返回一個鍵-值對映關係,它與大於等於給定鍵的最小鍵關聯;如果不存在這樣的鍵,則返回 null。 K ceilingKey(K key) 返回大於等於給定鍵的最小鍵;如果不存在這樣的鍵,則返回 null。 void clear() 從此對映中移除所有對映關係。 Object clone() 返回此 TreeMap 例項的淺表副本。 Comparator<? super K> comparator() 返回對此對映中的鍵進行排序的比較器;如果此對映使用鍵的自然順序,則返回 null。 boolean containsKey(Object key) 如果此對映包含指定鍵的對映關係,則返回 true。 boolean containsValue(Object value) 如果此對映為指定值對映一個或多個鍵,則返回 true。 NavigableSet<K> descendingKeySet() 返回此對映中所包含鍵的逆序 NavigableSet 檢視。 NavigableMap<K,V> descendingMap() 返回此對映中所包含對映關係的逆序檢視。 Set<Map.Entry<K,V>> entrySet() 返回此對映中包含的對映關係的 Set 檢視。 Map.Entry<K,V> firstEntry() 返回一個與此對映中的最小鍵關聯的鍵-值對映關係;如果對映為空,則返回 null。 K firstKey() 返回此對映中當前第一個(最低)鍵。 Map.Entry<K,V> floorEntry(K key) 返回一個鍵-值對映關係,它與小於等於給定鍵的最大鍵關聯;如果不存在這樣的鍵,則返回 null。 K floorKey(K key) 返回小於等於給定鍵的最大鍵;如果不存在這樣的鍵,則返回 null。 V get(Object key) 返回指定鍵所對映的值,如果對於該鍵而言,此對映不包含任何對映關係,則返回 null。 SortedMap<K,V> headMap(K toKey) 返回此對映的部分檢視,其鍵值嚴格小於 toKey。 NavigableMap<K,V> headMap(K toKey, boolean inclusive) 返回此對映的部分檢視,其鍵小於(或等於,如果 inclusive 為 true)toKey。 Map.Entry<K,V> higherEntry(K key) 返回一個鍵-值對映關係,它與嚴格大於給定鍵的最小鍵關聯;如果不存在這樣的鍵,則返回 null。 K higherKey(K key) 返回嚴格大於給定鍵的最小鍵;如果不存在這樣的鍵,則返回 null。 Set<K> keySet() 返回此對映包含的鍵的 Set 檢視。 Map.Entry<K,V> lastEntry() 返回與此對映中的最大鍵關聯的鍵-值對映關係;如果對映為空,則返回 null。 K lastKey() 返回對映中當前最後一個(最高)鍵。 Map.Entry<K,V> lowerEntry(K key) 返回一個鍵-值對映關係,它與嚴格小於給定鍵的最大鍵關聯;如果不存在這樣的鍵,則返回 null。 K lowerKey(K key) 返回嚴格小於給定鍵的最大鍵;如果不存在這樣的鍵,則返回 null。 NavigableSet<K> navigableKeySet() 返回此對映中所包含鍵的 NavigableSet 檢視。 Map.Entry<K,V> pollFirstEntry() 移除並返回與此對映中的最小鍵關聯的鍵-值對映關係;如果對映為空,則返回 null。 Map.Entry<K,V> pollLastEntry() 移除並返回與此對映中的最大鍵關聯的鍵-值對映關係;如果對映為空,則返回 null。 V put(K key, V value) 將指定值與此對映中的指定鍵進行關聯。 void putAll(Map<? extends K,? extends V> map) 將指定對映中的所有對映關係複製到此對映中。 V remove(Object key) 如果此 TreeMap 中存在該鍵的對映關係,則將其刪除。 int size() 返回此對映中的鍵-值對映關係數。 NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) 返回此對映的部分檢視,其鍵的範圍從 fromKey 到 toKey。 SortedMap<K,V> subMap(K fromKey, K toKey) 返回此對映的部分檢視,其鍵值的範圍從 fromKey(包括)到 toKey(不包括)。 SortedMap<K,V> tailMap(K fromKey) 返回此對映的部分檢視,其鍵大於等於 fromKey。 NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) 返回此對映的部分檢視,其鍵大於(或等於,如果 inclusive 為 true)fromKey。 Collection<V> values() 返回此對映包含的值的 Collection 檢視。
第2部分 TreeMap資料結構
TreeMap的繼承關係
java.lang.Object
java.util.AbstractMap<K, V>
java.util.TreeMap<K, V>
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable {}
TreeMap與Map關係如下圖:
從圖中可以看出:
(01) TreeMap實現繼承於AbstractMap,並且實現了NavigableMap介面。
(02) TreeMap的本質是R-B Tree(紅黑樹),它包含幾個重要的成員變數: root, size, comparator。
root 是紅黑數的根節點。它是Entry型別,Entry是紅黑數的節點,它包含了紅黑數的6個基本組成成分:key(鍵)、value(值)、left(左孩子)、right(右孩子)、parent(父節點)、color(顏色)。Entry節點根據key進行排序,Entry節點包含的內容為value。
紅黑數排序時,根據Entry中的key進行排序;Entry中的key比較大小是根據比較器comparator來進行判斷的。
size是紅黑數中節點的個數。
第3部分 TreeMap遍歷方式
3.1 遍歷TreeMap的鍵值對
第一步:根據entrySet()獲取TreeMap的“鍵值對”的Set集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。
// 假設map是TreeMap物件
// map中的key是String型別,value是Integer型別
Integer integ = null;
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next();
// 獲取key
key = (String)entry.getKey();
// 獲取value
integ = (Integer)entry.getValue();
}
3.2 遍歷TreeMap的鍵
第一步:根據keySet()獲取TreeMap的“鍵”的Set集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。
// 假設map是TreeMap物件
// map中的key是String型別,value是Integer型別
String key = null;
Integer integ = null;
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
// 獲取key
key = (String)iter.next();
// 根據key,獲取value
integ = (Integer)map.get(key);
}
3.3 遍歷TreeMap的值
第一步:根據value()獲取TreeMap的“值”的集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。
// 假設map是TreeMap物件
// map中的key是String型別,value是Integer型別
Integer value = null;
Collection c = map.values();
Iterator iter= c.iterator();
while (iter.hasNext()) {
value = (Integer)iter.next();
}
2. HashMap的介紹和使用
HashMap簡介
HashMap 是一個散列表,它儲存的內容是鍵值對(key-value)對映。
HashMap 繼承於AbstractMap,實現了Map、Cloneable、java.io.Serializable介面。
HashMap 的實現不是同步的,這意味著它不是執行緒安全的。它的key、value都可以為null。此外,HashMap中的對映不是有序的。
HashMap 的例項有兩個引數影響其效能:“初始容量” 和 “載入因子”。容量 是雜湊表中桶的數量,初始容量 只是雜湊表在建立時的容量。載入因子 是雜湊表在其容量自動增加之前可以達到多滿的一種尺度。當雜湊表中的條目數超出了載入因子與當前容量的乘積時,則要對該雜湊表進行 rehash 操作(即重建內部資料結構),從而雜湊表將具有大約兩倍的桶數。
通常,預設載入因子是 0.75, 這是在時間和空間成本上尋求一種折衷。載入因子過高雖然減少了空間開銷,但同時也增加了查詢成本(在大多數 HashMap 類的操作中,包括 get 和 put 操作,都反映了這一點)。在設定初始容量時應該考慮到對映中所需的條目數及其載入因子,以便最大限度地減少 rehash 操作次數。如果初始容量大於最大條目數除以載入因子,則不會發生 rehash 操作。
HashMap的繼承關係
HashMap與Map關係如下圖:
HashMap的建構函式
HashMap共有4個建構函式,如下:
複製程式碼 程式碼如下:
// 預設建構函式。
HashMap()
// 指定“容量大小”的建構函式
HashMap(int capacity)
// 指定“容量大小”和“載入因子”的建構函式
HashMap(int capacity, float loadFactor)
// 包含“子Map”的建構函式
HashMap(Map<? extends K, ? extends V> map)
HashMap的API
複製程式碼 程式碼如下:
void clear()
Object clone()
boolean containsKey(Object key)
boolean containsValue(Object value)
Set<Entry<K, V>> entrySet()
V get(Object key)
boolean isEmpty()
Set<K> keySet()
V put(K key, V value)
void putAll(Map<? extends K, ? extends V> map)
V remove(Object key)
int size()
Collection<V> values()
3. HashMap,LinkedHashMap,TreeMap的有序性
HashMap 是將 Key 做 Hash 演算法,然後將 Hash 值對映到記憶體地址,直接取得 Key 所對應的資料。在 HashMap 中,底層資料結構使用的是陣列,所謂的記憶體地址即陣列的下標索引。HashMap 的高效能需要保證以下幾點:
Hash 演算法必須是高效的;
Hash 值到記憶體地址 (陣列索引) 的演算法是快速的;
根據記憶體地址 (陣列索引) 可以直接取得對應的值。
HashMap 實際上是一個連結串列的陣列。基於 HashMap 的連結串列方式實現機制,只要 HashCode() 和 Hash() 方法實現得足夠好,能夠儘可能地減少衝突的產生,那麼對 HashMap 的操作幾乎等價於對陣列的隨機訪問操作,具有很好的效能。但是,如果 HashCode() 或者 Hash() 方法實現較差,在大量衝突產生的情況下,HashMap 事實上就退化為幾個連結串列,對 HashMap 的操作等價於遍歷連結串列,此時效能很差。
HashMap 的一個功能缺點是它的無序性,被存入到 HashMap 中的元素,在遍歷 HashMap 時,其輸出是無序的。如果希望元素保持輸入的順序,可以使用 LinkedHashMap 替代。
LinkedHashMap 繼承自 HashMap,具有高效性,同時在 HashMap 的基礎上,又在內部增加了一個連結串列,用以存放元素的順序。
HashMap 通過 hash 演算法可以最快速地進行 Put() 和 Get() 操作。TreeMap 則提供了一種完全不同的 Map 實現。從功能上講,TreeMap 有著比 HashMap 更為強大的功能,它實現了 SortedMap 介面,這意味著它可以對元素進行排序。TreeMap 的效能略微低於 HashMap。如果在開發中需要對元素進行排序,那麼使用 HashMap 便無法實現這種功能,使用 TreeMap 的迭代輸出將會以元素順序進行。LinkedHashMap 是基於元素進入集合的順序或者被訪問的先後順序排序,TreeMap
則是基於元素的固有順序 (由 Comparator 或者 Comparable 確定)。
LinkedHashMap 是根據元素增加或者訪問的先後順序進行排序,而 TreeMap 則根據元素的 Key 進行排序。
測試程式碼:
package mapKeySet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* 2015年4月9日下午3:33:44
* @version 1.0
*/
public class KeySetTest {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("a", "1");
map.put("b", "2");
map.put("c", "3");
map.put("d", "4");
System.out.print("HashMap:");
for(String key : map.keySet()) {
System.out.print(map.get(key) + " ");
}
Map<String, String> linkedMap = new LinkedHashMap<String, String>();
linkedMap.put("a", "1");
linkedMap.put("b", "2");
linkedMap.put("c", "3");
linkedMap.put("d", "4");
System.out.print("LinkedHashMap:");
for(String key : linkedMap.keySet()) {
System.out.print(linkedMap.get(key) + " ");
}
Map<String, String> treeMap = new TreeMap<String, String>();
treeMap.put("a", "1");
treeMap.put("b", "2");
treeMap.put("c", "3");
treeMap.put("d", "4");
System.out.print("TreeMap:");
for(String key : treeMap.keySet()) {
System.out.print(treeMap.get(key) + " ");
}
}
}
輸出結果:
HashMap:4 2 3 1 LinkedHashMap:1 2 3 4 TreeMap:1 2 3 4
4. 二維HashMap
宣告:
HashMap<String,HashMap<String,Integer>> map1;
新增元素:
HashMap<String,Integer> temp_hash=new HashMap<String,Integer>();
temp_hash.put(string1, 1);
map1.put(string2,temp_hash);
遍歷:
申請一個迭代器:
Iterator<String> it1=map1.keySet().iterator();
訪問:
while(it_verb.hasNext()){
String temp=it.next();
HashMap<String,Integer> temp_map=verb_map.get(temp);
}
// 程式碼實現
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
public class TestTreeMap {
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeMap<Integer, String> map = new TreeMap<>();
map.put(3, "aa");
map.put(2, "bb");
map.put(4, "cc");
map.put(1, "dd");
map.put(5, "dd");
map.put(6, "ee");
System.out.print("original: ");
Iterator it = map.entrySet().iterator();
while(it.hasNext()) {
Map.Entry e = (Map.Entry)it.next();
int key = (Integer)e.getKey();
String val = (String)e.getValue();
System.out.print(key + " " + val + (it.hasNext() ? ", " : "."));
}
System.out.println();
System.out.println("-------------------------------");
map.remove(6);
System.out.print("after remove: ");
it = map.entrySet().iterator();
while(it.hasNext()) {
Map.Entry e = (Map.Entry)it.next();
int key = (Integer)e.getKey();
String val = (String)e.getValue();
System.out.print(key + " " + val + (it.hasNext() ? ", " : "."));
}
System.out.println();
System.out.println("-------------------------------");
System.out.println(3 + "'s val: " + map.get(3));
System.out.println(3 + "'s higherKey: " + map.higherKey(3));
Map.Entry tmp1 = map.higherEntry(3);
System.out.println(3 + "'s higherEntry: " + (Integer)tmp1.getKey() + " " + (String)tmp1.getValue());
System.out.println(3 + "'s lowerKey: " + map.lowerKey(3));
Map.Entry tmp2 = map.lowerEntry(3);
System.out.println(3 + "'s lowerEntry: " + (Integer)tmp2.getKey() + " " + (String)tmp2.getValue());
System.out.println("firstKey: " + map.firstKey() + ", " + "lastKey: " + map.lastKey());
System.out.println();
TestUserDefined();
TestTwoDimensional();
}
private static class Node implements Comparable<Node> {
int a, b;
public Node(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public int compareTo(Node o) {
// TODO Auto-generated method stub
if(this.a == o.a) {
return (this.b < o.b) ? 1 : -1;
}
else {
return (this.a > o.a) ? 1 : -1;
}
}
public String toString() {
return "{ " + this.a + "," + this.b + " }";
}
}
private static class mycmp implements Comparator<Node> {
@Override
public int compare(Node o1, Node o2) {
// TODO Auto-generated method stub
if(o1.a == o2.a) {
return o1.b - o2.b;
}
return o2.a - o1.a;
}
}
private static void TestUserDefined() {
// By Comparable介面
TreeMap<Node, String> map1 = new TreeMap<>();
map1.put(new Node(3, 2), "aa");
map1.put(new Node(3, 4), "bb");
map1.put(new Node(5, 8), "cc");
map1.put(new Node(5, 7), "dd");
map1.put(new Node(6, 9), "ee");
Iterator it1 = map1.entrySet().iterator();
while(it1.hasNext()) {
Map.Entry e = (Map.Entry)it1.next();
System.out.println((Node)e.getKey() + ": " + (String)e.getValue());
}
System.out.println();
System.out.println("-------------------------------");
System.out.println();
// By Comparator介面
TreeMap<Node, String> map2 = new TreeMap<>(new mycmp());
map2.put(new Node(3, 2), "aa");
map2.put(new Node(3, 4), "bb");
map2.put(new Node(5, 8), "cc");
map2.put(new Node(5, 7), "dd");
map2.put(new Node(6, 9), "ee");
Iterator it2 = map2.entrySet().iterator();
while(it2.hasNext()) {
Map.Entry e = (Map.Entry)it2.next();
System.out.println((Node)e.getKey() + ": " + (String)e.getValue());
}
System.out.println();
}
private static void TestTwoDimensional() {
TreeMap<Integer, TreeMap<Integer, String>> mmp = new TreeMap<>();
TreeMap<Integer, String> tmpMap = new TreeMap<>();
tmpMap.put(1, "a");
tmpMap.put(3, "b");
tmpMap.put(2, "c");
mmp.put(1, tmpMap);
mmp.put(3, tmpMap);
mmp.put(2, tmpMap);
Iterator it = mmp.entrySet().iterator();
while(it.hasNext()) {
Map.Entry e = (Map.Entry)it.next();
int key = (Integer)e.getKey();
tmpMap = (TreeMap<Integer, String>)e.getValue();
System.out.print(key + "-> { ");
Iterator tmpIt = tmpMap.entrySet().iterator();
while(tmpIt.hasNext()) {
Map.Entry tmpe = (Map.Entry)tmpIt.next();
int tmpkey = (Integer)tmpe.getKey();
String tmpval = (String)tmpe.getValue();
System.out.print(tmpkey + " " + tmpval + (tmpIt.hasNext() ? ", " : ""));
}
System.out.println(" }");
}
}
}
繼續加油~