1. 程式人生 > 其它 >高項-第二十一章 專案組合管理

高項-第二十一章 專案組合管理

簡介:

在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.