高項-第二十一章 專案組合管理
簡介:
在Collection介面中,儲存的資料都是單個的物件,在資料結構中除了單個物件的資料,也可以進行二元偶物件的儲存(key=value)的形式來儲存,而儲存二元偶物件的核心意義在於:通過key獲取對應的value。
在開發中:Collection集合儲存資料的目的是為了輸出,而Map集合儲存資料的目的是為了key的查詢。
Map介面:
Map介面是進行二元偶物件儲存的最大父介面,該介面定義:
public interface Map<K,V>;
該介面為一個獨立的父介面,並且在進行介面物件例項化的 時候需要設定Key與Value的型別,所以在儲存的時候需要兩個內容,在Map介面中定義有許多操作方法,但是有幾個方法需要牢記:
- 向集合中儲存資料:public V put(K key, V value); - 根據Key查詢資料:public V get(Object key); - 將Map集合轉為Set集合:public Set<Map.Entry<K, V>> entrySet(); - 查詢指定的Key是否存在:public boolean containsKey(Object key); - 將Map集合中的Key轉為Set集合:public Set<K> keySet(); - 根據Key刪除指定的資料:public V remove(Object key);
紅色的是重要方法。
從JDK1.9之後Map集合提供了一些靜態方法供使用者使用。
程式碼實現:
import java.util.Map;
public class MAIN {
public static void main(String[] args) {
Map<String, Integer> map = Map.of("one",1,"two",2);
System.out.println(map);
}
}
輸出結果:
在Map集合中資料的儲存按照形式“Key=Value”,並且使用of()方法操作,裡面資料的Key是不允許重複的和為空。
正常的使用情況是使用Map的子類進行,常用的子類有:HashMap、HashTable、TreeMap、LinkedHashMap
Map介面子類 - HashMap類:
HashMap是Map介面中最為常見的一個子類,該類的特點是無序儲存。
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
繼承形式符合集合定義的形式,提供又要抽象類,需要重複實現Map介面。
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
|
當使用無參構造的時候會出現有一個loadFactor屬性,並且該屬性預設的內容是“0.75f”
static final float DEFAULT_LOAD_FACTOR = 0.75f;
|
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
|
在使用put()方法進行資料儲存的時候會呼叫putVal()方法,同時會將Key進行hash處理(生成一個hash碼),而對於putVal()方法會發現提供了一個節點類進行資料儲存,而在使用putVal()方法操作的過程之中會呼叫一個resize()的方法可以進行容量的擴充。 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
static class Node<K,V> implements Map.Entry<K,V>
|
面試題:
在進行HashMap的put()操作的時候如何實現容量擴充的?
- 在HashMap類裡面專門提供有一個“ DEFAULT_INITIAL_CAPACITY ”常量,作為初始化容量配置,而後這個常量的預設大小為16個元素,預設可以儲存最大長度是16;
- 當儲存的內容的容量超過了閾值( DEFAULT_LOAD_FACTOR = 0.75f ),相當於 “ 容量 * 閾值 = 12 ” 儲存12個元素的時候就會進行容量的擴充。
- 在進行擴充的時候HashMap採用的是成倍的擴充模式,即:每一次都擴充2倍的容量
面試題:
請解釋HashMap的工作原理?(JDK1.8之後)
- 在HashMap之中進行資料儲存的依然是利用Node類完成的,那麼這種情況下就證明可以使用的資料結構有兩種:連結串列(時間複雜度“O(n)”)、二叉樹(時間複雜度(“O(logn)”));
- 從JDK1.8開始,HashMap的實現出現了改變,因為其要適應於大資料時代的海量資料,所以對於其儲存發生了變化,並且在HashMap類中提供了一個重要的常量:
static final int TREEIFY_THRESHOLD = 8;
在使用HashMap進行資料儲存的時候,如果儲存的資料沒有超過閾值“ 8 ”,那麼會按照連結串列的形式進行儲存,如果超出了閾值,則會將連結串列轉換為紅黑樹以實現樹的平衡,並且利用左旋與右旋保證資料的查詢效能。
Map集合的使用:
import java.util.HashMap;
import java.util.Map;
public class MAIN {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("1", 1);
map.put("2", 2);
map.put("3", 3);
map.put("4", 4);
map.put("4", 3); // Key重複
map.put(null, 0); // Key為空
map.put("5", null); // Value為空
System.out.println(map.get(null)); // Key不存在
System.out.println(map.get("4")); // Key存在
System.out.println(map.get("5")); // Key存在
}
}
輸出結果:
可以發現:
Key不存在也能獲取對應的Value
而出現重複Key的時候,後面的Key會覆蓋前面的Key
只要Key存在就能獲取Value,即使Value為空
Put()方法的返回值觀察:
import java.util.HashMap;
import java.util.Map;
public class MAIN {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
System.out.println(map.put("1", 1)); // Key不重複,返回null
System.out.println(map.put("1", 101)); // Key重複,返回舊資料
}
}
輸出結果:
在設定了相同Key內容的時候,put()方法會返回舊的Value.