Java集合 Map 集合 與 操作集合的工具類: Collections 的詳細說明

来源:https://www.cnblogs.com/TheMagicalRainbowSea/archive/2023/02/03/17090200.html
-Advertisement-
Play Games

Map 介面與 Collection 併列存在的,用於保存具有映射關係的數據:key-value 被稱為 鍵值對 。Java集合可分為 Collection 和 Map 兩種體系。Map 中的 key 和 value 都可以是任何引用類型的數據。Map 中的 key 用 Set 集合存儲的,不允許... ...


Java集合 Map 集合 與 操作集合的工具類: Collections 的詳細說明

在這裡插入圖片描述


每博一文案

別把人生,輸給心情
師父說:心情不是人生的全部,卻能左右人生的全部。
你有沒有體會到,當你心情好的時候,生活仿佛陽光燦爛,順風順水,
當你心情不好的時候,似乎周圍的一切都糟糕透了。
有時候,我們不是輸給了別人,而是敗給了壞心情的自己。
人活著就像一個陀螺,為了生活不停的轉動,永遠都有忙不完的事。
有時候又像沙漠中的駱駝,背負著重擔努力地前行,卻不知道哪裡才是終點。
先現在情緒低落,只是因為陷進了自我糾纏的陷阱,等到熬過了這段苦難,
你會發現你所糾結的東西,真的只是無關痛癢的小事。
生活就像天氣,不會總是晴天,也不會一直陰雨,喜歡和討厭是次要的,關鍵是你要學會調整自己。
心靜了,才能聽見自己的心聲,心清了,才能照見萬物的本性。
假如任由壞情緒累積和蔓延,很多事只會變得越來越糟糕,
既然做不到讓所有人都滿意,為何不努力讓自己開心?
生活是你自己的,喜怒悲歡都由你自己決定,記得別被壞情緒束縛住,
不要讓你的人生,輸給了心情。
                                      ——————   一禪心靈廟語

目錄

1. Map介面概述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

  • Map 介面與 Collection 併列存在的,用於保存具有映射關係的數據:key-value 被稱為 鍵值對

  • Java集合可分為 Collection 和 Map 兩種體系。

    • Collection 介面:單例數據,定義了存取一組對象的方法的集合。
      • List : 元素有序,可重覆的集合。
      • Set :元素無序,不可重覆的集合。
    • Map 介面:雙列數據,保存具有映射關係”key-value對“ 的集合。
  • Map 中的 keyvalue 都可以是任何引用類型的數據。

    • keyvalue 都是引用數據類型,都是存儲對象的記憶體地址的。不是基本數據類型。
    • 其中 key 起到主導地位,valuekey 的一個附屬品。
  • Map 中的 keySet 集合存儲的,不允許重覆。即同一個 Map 對象所對應的類,必須重寫hashCode() 和 equals() 方法。但是其中的 value 值是可以存儲重覆的數據的。而 value 值則是被 Collection 介面集合存儲的。

  • 常用 String 類作為 Map 的 ”鍵“。

  • keyvalue 之間存在單向一對一關係,即通過指定的 key 總能找到唯一的,確定的 value

  • Map 介面的常用實現類:

    • HashMap 作為Map的主要實現類,線程不安全的,效率高,可以存儲 null 的key 和 value。HashMap是 Map 介面使用頻率最高的實現類
    • LinkedHashMap 保證再遍歷Map元素時,可以按照添加的順序實現遍歷,原因: 在原有的HashMap底層結構基礎上,添加了一對指針,指向前一個和後一個元素。對於頻繁的遍歷操作,此類執行效率高於HashMap
    • TreeMap 保證按照添加的 key-value鍵值對進行排序,實現排序遍歷.此時考慮key的自然排序或定製排序,底層使用紅黑樹:
    • Hashtalbe 作為古老的實現類,線程安全的,效率低,不可以存儲 null
    • Properties 主要用於配置文件的讀取。
  • 鍵值對的示圖:

在這裡插入圖片描述

2. Map介面:常用方法

添加、刪除、修改操作:

  • put(K key, V value) : 將指定的 key 和 value 值添加/修改到該集合當中。
V put(K key,V value);  // 將指定的 key 和 value 值添加/修改到該集合當中。
  • putAll(Map m) : 將 m 中所有的key-value 值存放到當前 對象集合當中。
void putAll(Map<? extends K,? extends V> m);  // 將m中的所有key-value對存放到當前map集合當中
  • remove(Object key) : 移除指定key的key-value對,並返回value。
V remove(Object key);  // 移除指定key的key-value對,並返回value
  • clear() : 清空當前map中的所有數據。
void clear();  // 清空當前map中的所有數據
  • size() : 返回此集合中存儲的元素數據(鍵值對)的數量。
int size();  // 返回此集合中存儲的元素數據(鍵值對)的數量。

舉例:

import java.util.HashMap;
import java.util.Map;

public class MapTest {
    public static void main(String[] args) {
        // Map 介面 , HashMap實現類,多態,<String,Integer> 泛型
        Map<String,Integer> map = new HashMap<String,Integer>();

        // 添加元素數據:
        map.put("zhangsan",66);
        map.put("lisi",89);
        map.put("wangwu",97);
        map.put("lihua",99);

        System.out.println(map);
        int size = map.size();  // 返回該集合中存儲的鍵值對的數量。
        System.out.println(size);

        System.out.println("*********************");

        Integer zhangsan = map.remove("zhangsan");  // 移除key = zhangsan的元素數據,並返回該移除的value值。
        System.out.println(zhangsan);
        System.out.println(map);

        map.clear();  // 清空該Map 集合當中的存儲的元素數據
        System.out.println(map.size());


    }
}

在這裡插入圖片描述

舉例

import java.util.HashMap;
import java.util.Map;

public class MapTest {
    public static void main(String[] args) {
        // Map 介面 , HashMap實現類,多態,<String,Integer> 泛型
        Map<String,Integer> map = new HashMap<String,Integer>();

        // 添加元素數據:
        map.put("zhangsan",66);
        map.put("lisi",89);
        
        Map<String,Integer> map2 = new HashMap<String,Integer>();
        map2.put("wangwu",97);
        map2.put("lihua",99);
        
        map.putAll(map2);  // 將 map2 集合中所有的key-value鍵值對添加到此 map集合當中去
                           // 註意:兩者集合存儲的元素數據類型必須是一致的才可以添加成功。
        System.out.println(map);
    }
}

在這裡插入圖片描述

元素查詢的操作:

  • get(Object key) : 獲取指定key對應的value。
V get(Object key);  // 獲取指定key對應的value
  • containsKey(Object key) : 判斷該集合當中是否包含指定的 key值。
boolean containsKey(Object key);  // 判斷該集合當中是否包含指定的 key 值。
  • containsValue(Object key) : 判斷該集合當中是否包含指定的 value 值。
boolean containsValue(Object value); // 判斷判斷該集合當中是否包含指定的 value 值。
  • isEmpty() : 判斷此集合是否為 空,是返回 true,不是返回 false。
boolean isEmpty();  // 判斷此集合是否為 空,是返回 true,不是返回 false;
  • equals(Object o) : 判斷當前map和參數對象 o 是否相等。
boolean equals(Object o); // 判斷當前map和參數對象 o 是否相等

舉例:

import java.util.HashMap;
import java.util.Map;

public class MapTest {
    public static void main(String[] args) {
        // Map 介面 , HashMap實現類,多態,<String,Integer> 泛型
        Map<String,Integer> map = new HashMap<String,Integer>();

        // 添加元素數據:
        map.put("zhangsan",66);
        map.put("lisi",89);
        map.put("wangwu",97);
        map.put("lihua",99);

        System.out.println(map.get("zhangsan"));  // 獲取到對應 key上的 value值
        System.out.println(map.containsKey("zhangsan"));  // 判斷該集合當中是否存在 key = zhangsan的鍵值對
        System.out.println(map.containsValue(99));  // 判斷該集合當中是否存在 value = 99的鍵值對
        System.out.println(map.isEmpty());  // 判斷該集合是否為空
        System.out.println(map.equals(map)); //  判斷當前map和參數對象 o 是否相等

    }
}

在這裡插入圖片描述

元視圖操作的方法:

  • keySet() : 返回所有key構成的Set集合。從該方法中可以看出 Map 介面下的集合中的 key 值是存儲在 Set 介面集合當中的。
Set<K> keySet();  // 返回所有key構成的Set集合
  • values() : 返回所有value構成的Collection集合。從該方法中可以看出 Map 介面下的集合中的 value 值是存儲在 Collection 介面集合當中的。
Collection<V> values();  // 返回所有value構成的Collection集合

舉例:

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapTest {
    public static void main(String[] args) {
        // Map 介面 , HashMap實現類,多態,<String,Integer> 泛型
        Map<String,Integer> map = new HashMap<String,Integer>();

        // 添加元素數據:
        map.put("zhangsan",66);
        map.put("lisi",89);
        map.put("wangwu",97);
        map.put("lihua",99);

        Set<String> keys = map.keySet();  // 返回此集合當中所有的 key 值存儲到 Set 集合當中
        for (String s : keys) {
            System.out.println(s);
        }

        System.out.println("****************");
        Collection<Integer> values = map.values();  // 返回此集合當中所有的 value 值存儲到 Collection 集合當中
        // Collection 介面集合可以使用迭代器
        Iterator<Integer> iterator = values.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

在這裡插入圖片描述

  • entrySet() : 返回該集合當中的所有 key-value鍵值對,並存儲到 Set 集合當中後,再返回一個 Set 集合對象(該集合存儲了所有的key-value) 值。
Set<Map.Entry<K,V>> entrySet();  // 返回所有key-value對構成的Set集合

其中的 Map.Entry 表示的是一個介面,也可以理解為是一個類。

*   Set<Map.Entry<K,V>> entrySet() 將 Map集合轉換成 Set集合
*   假設現在有一個 Map集合 ,如下所示:
*     map1 集合對象
*     key               value
*     1                 zhangsan
*     2                 lisi
*     3                 wangwu
*     4                 zhaoliu
*
*     Set set = mop1.entrySet();
*     set 集合對象
*     1=zhangsan
*     2=lisi
*     3=wangwu
*     4=zhaoliu

Map.Entry<K,V> 的圖示:

在這裡插入圖片描述

在這裡插入圖片描述

舉例:

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapTest {
    public static void main(String[] args) {
        // Map 介面 , HashMap實現類,多態,<String,Integer> 泛型
        Map<String,Integer> map = new HashMap<String,Integer>();

        // 添加元素數據:
        map.put("zhangsan",66);
        map.put("lisi",89);
        map.put("wangwu",97);
        map.put("lihua",99);

        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
        while(iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            // getKey()獲取 key 值,getValue()獲取value值
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }
    }
}

在這裡插入圖片描述

3. Map實現類之一:HashMap

在這裡插入圖片描述
在這裡插入圖片描述

在這裡插入圖片描述

  • HashMapMap 介面使用頻率最高的實現類。

  • HashMap 允許存儲 null 值,key 可以為 null ,但僅僅只能有一個,因為不可重覆,value 可以為 null 。無序

  • HashMap 中所有的 key 構成的集合是存儲在 Set 當中的,無序的,不可重覆的,所以:key 所在類和 Set 集合是一樣的必須重寫 euqlas() 和 hashCode() 方法。其中 Java當中的包裝類和String 類都重寫了 equals() 和 hashCode()方法。基本上只有我們自定的類需要重寫。

  • 一個key-value 構成一個 Map.Entry

  • 所有的 Map.Entry 構成的集合是 Set 無序的,不可重覆的。

  • HashMap 判斷兩個 key 相等的標準是 : 兩個key 通過 equals() 方法返回 true , hashCode 值也相等。

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

  • HashMap 集合底層是哈希表的數據結構

    • 哈希表是一個數組 + 單向鏈表 的結合體。
    • 數組:在查詢方面效率很高,隨機增刪方面很低。
    • 鏈表:在隨機增刪方面效率較高,在查詢方面效率低。
    • 而哈希表:將以上兩種數據結構融合在一起,充分發揮它們各自的優點。
  • 對於 HashMap 中的方法基本上都是繼承了對應的 Map 介面的方法,上面已經說明瞭,這裡就不多介紹了。

舉例:

如下是 Set 中的 Key 存儲自定義類 Person5 ,其中並沒有重寫Object 中的 equals() 方法和 hashCode()方法。會出現 Key 存儲到重覆的數據。

package blogs.blogs7;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class HashMapTest {
    public static void main(String[] args) {
        HashMap<Person5,Integer> hashMap = new HashMap<Person5, Integer>();
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("zhangsan",23),4);
        hashMap.put(new Person5("lihua",20),5);

       // 遍歷HashMap 集合
        Set<Map.Entry<Person5,Integer>> entries = hashMap.entrySet();
        for (Map.Entry<Person5,Integer> entry : entries) {
            System.out.println(entry);
        }

    }
}


class Person5 {
    String name;
    int age;

    public Person5() {
    }

    public Person5(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person5{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在這裡插入圖片描述


修改: 重寫其中的 Key 值的 Set 集合中存儲的 類中的 equals() 和 hashCode() 方法。

package blogs.blogs7;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class HashMapTest {
    public static void main(String[] args) {
        HashMap<Person5,Integer> hashMap = new HashMap<Person5, Integer>();
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("zhangsan",23),4);
        hashMap.put(new Person5("lihua",20),5);

       // 遍歷HashMap 集合
        Set<Map.Entry<Person5,Integer>> entries = hashMap.entrySet();
        for (Map.Entry<Person5,Integer> entry : entries) {
            System.out.println(entry);
        }

    }
}


class Person5 {
    String name;
    int age;

    public Person5() {
    }

    public Person5(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person5)) return false;
        Person5 person5 = (Person5) o;
        return age == person5.age &&
                Objects.equals(name, person5.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Person5{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在這裡插入圖片描述

HashMap中的 Key值可以存儲添加 null 值,但是僅僅只能添加一個 null ,因為 Key 中的數據存儲在 Set集合當中的,不可重覆,而 Value 值也可以存儲 null值,而且可以存儲多個 null 值,因為 Value 值數據底層是存儲在Collection集合當中的

舉例:

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class HashMapTest {
    public static void main(String[] args) {
        HashMap<String,Integer> hashMap = new HashMap<String, Integer>();
        hashMap.put(null,null);
        hashMap.put(null,null);
        hashMap.put(null,null);
        hashMap.put("1",null);
        hashMap.put("2",null);
        hashMap.put("3",null);

        // 遍歷HashMap 集合
        Set<Map.Entry<String,Integer>> entries = hashMap.entrySet();
        for (Map.Entry<String,Integer> entry : entries) {
            System.out.println(entry);
        }
    }
}

在這裡插入圖片描述

常用方法總結:

  • 添加: put(Object key,Object value)
  • 刪除: remove(object key)
  • **修改: **put(Object key,Object value)
  • 查詢: get(Object key)
  • 長度: size();
  • 遍歷: keySet()/values()/entrySet()

3.1 HashMap的存儲結構

JDK 7及以前版本:HashMap是數組+鏈表結構(即為鏈地址法)

JDK 8版本發佈以後:HashMap是數組+鏈表+紅黑樹實現

在這裡插入圖片描述

如下是 JDK8 的HashMap 結構圖

在這裡插入圖片描述

3.2 HashMap源碼中的重要常量

/**
 * The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16  HashMap的預設容量是 16
-----------------------------------------------------------------------------------
 /**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;  // HashMap的最大支持容量,2^30
-----------------------------------------------------------------------------------
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;  // HashMap的預設載入因數
-----------------------------------------------------------------------------------
 /**
* The bin count threshold for using a tree rather than list for a
* bin.  Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/
static final int TREEIFY_THRESHOLD = 8;  // Bucket中鏈表長度大於該預設值,轉化為紅黑樹
-----------------------------------------------------------------------------------
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/
static final int UNTREEIFY_THRESHOLD = 6; // Bucket中紅黑樹存儲的Node小於該預設值,轉化為鏈表
-----------------------------------------------------------------------------------
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
* between resizing and treeification thresholds.
*/
static final int MIN_TREEIFY_CAPACITY = 64; // 桶中的Node被樹化時最小的hash表容量。(當桶中Node的數量大到需要變紅黑樹時,若hash表容量小於MIN_TREEIFY_CAPACITY時,此時應執行resize擴容操作這個MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4倍。)
-----------------------------------------------------------------------------------
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;  // 存儲元素的數組,總是2的n次冪
-----------------------------------------------------------------------------------
/**
* Holds cached entrySet(). Note that AbstractMap fields are used
* for keySet() and values().
*/
transient Set<Map.Entry<K,V>> entrySet;  // 存儲具體元素的集合
-----------------------------------------------------------------------------------
/**
* The number of key-value mappings contained in this map.
*/
transient int size;  // HashMap中實際存儲的鍵值對的數量
-----------------------------------------------------------------------------------
/**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
* rehash).  This field is used to make iterators on Collection-views of
* the HashMap fail-fast.  (See ConcurrentModificationException).
*/
transient int modCount;  // HashMap擴容和結構改變的次數。
-----------------------------------------------------------------------------------
/**
* The next size value at which to resize (capacity * load factor).
*
* @serial
*/
// (The javadoc description is true upon serialization.
// Additionally, if the table array has not been allocated, this
// field holds the initial array capacity, or zero signifying
// DEFAULT_INITIAL_CAPACITY.)
int threshold;  // 擴容的臨界值,=容量 * 填充因數
-----------------------------------------------------------------------------------
/**
* The load factor for the hash table.
*
* @serial
*/
final float loadFactor;  // 填充因數

3.3 HashMap的存儲結構:JDK 1.8之前 / JDK 1.8之後

3.3.1 JDk 1.8 之前

  • HashMap 內部存儲結構其實是 數組 + 鏈表的結合。當實例化一個 new HashMap() 時,實際上會創建一個長度為 CapacityEntry 數組。這個長度在 哈希表中稱為 容量(Capacity) ,在這個數組中可以存放元素的位置,我們稱之為 ”桶“ (bucket) ,每個 bucket 都有自己的索引,系統可以根據索引快速的查找 bucket 中的元素。

  • 每個bucket 中存儲一個元素,即 一個 Entry 對象內部類 ,但每一個 Entry 對象可以帶 一個引用變數,用於指向下一個元素,因此,在一個桶 (bucket) 中,就有可能生成一個 Entry 鏈。而且新添加的元素作為鏈表的 head

  • JDK7 源碼分析如下:

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

3.3.2 JDk 1.8 及之後

JDK8: HashMap 的內部存儲結構其實是:數組+鏈表+樹 的結合。當實例化一個 new HashMap 時,會初始化 initilCapacityloadFactor ,在 put() 第一對映射關係(鍵值對)添加時,系統會創建一個長度為 initilCapacityNode 數組 ,這個長度在哈希表中被稱為 ”容量" (Capacity),在這個數組中可以存放元素的位置,我們稱之為 “桶”(bucket) ,每個 bucket 都有自己的索引,系統可以根據索引快速的查找 bucket 中的元素。

每個bucket 中存儲一個元素數據,既 一個 Node 對象,但每一個 Node 對象可以帶一個引用變數 next ,用於指向下一個元素,因此,在一個桶中,就有可能生成一個 Node 鏈表。也可能是一個一個TreeNode 對象,每一個TreeNode 對象可以有兩個葉子節點 leftright ,因此,在一個桶中,就有可能生成一個 TreeNode 樹。而新添加的元素作為鏈表的 last ,或樹的葉子節點。

JDK1.8 源碼分析:

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

3.3.3 JDK8 HashMap 集合添加元素的過程

HashMap 集合中添加 put(key1,value1) 鍵值對, 首先調用 元素 key1 所在類的 hashCode() 方法,來得到該 key1對象的 hashCode(哈希) 值。

然後再根據得到的 hashCode (哈希)值,通過某種散列函數 計算除該對象在 HashSet 集合中底層Node[] 數組的存儲位置(即為:索引下標位置),(這個散列函數會與底層數組的長度相計算得到在數組中的下標,並且這種散列函數計算還儘可能保證能均勻存儲元素,越是散列分佈,該散列函數設計的越好)。

  • 判斷此計算處理得到的數組下標位置上是否已經有元素存儲了 :

    • 如果沒有其他元素數據存儲,則 元素 key1-value1添加到該位置上。 —— 情況1
    • 如果其它元素數據存儲(或以鏈表形式存儲的多個元素) : 則比較key1和已經存在的一個或多個數據的哈希值):
      • 如果 key1的hashCode() 哈希值與已經存在的數據的哈希值都 不相等, 則元素 key1-value1添加到該數組鏈表上。—— 情況2

      • 如果 key1 的hashCode() 哈希值 與 已經存在的數據的哈希值都 相等, 則調用 key1 元素所在類的 equals() 方法,, 判斷比較所存儲的內容是否和集合中存儲的相等。

        • 如果 不相等 也就是 equals() 方法,返回 false ,則此時 key1-value1添加成功。—— 情況3
        • 如果 相等 也就是 equals()方法,返回 true,不添加,替換掉其中存放的 value 值為 value1 ,因為 key1 是唯一的不可重覆的,但是其 對應的 value 值是可以重覆的。
  • 對應上述 添加成功的 情況2情況3 而言,關於情況2和情況3:此時key1-value1和原來的數據以鏈表的方式存儲

  • 如下是 添加鍵值對的過程的圖示:

在這裡插入圖片描述

如下是查找圖示:

在這裡插入圖片描述

假設將所有的hashCode()方法返回設定為不一樣的值,可以嗎?,有什麼問題:

不行,因為這樣的話,就導致 HashMap 集合底層的哈希表就成為了一維數組了,沒有鏈表的概念了,更沒有哈希表的概念了。

假設將所有的hashCode()方法返回設返回值固定為某個值,可以嗎?,有什麼問題:

答:不可以,設將所有的hashCode()方法,返回值固定為某個值,那麼會導致底層哈希表變成了純單向鏈表。這種情況下我們稱為:散列分別不均勻。

什麼時散列分佈不均勻

假設我們有 100 個元素,10個單向鏈表,那麼每個單向鏈表上有10個節點,這是最好的,是散列分佈均勻的

3.3.4 JDK8 HashMap 進行 "擴容"和 "樹形化"

擴容

put(Key1,value1) 添加鍵值對個數超過 數組大小(數組總大小 length ,不是數組中實際存放的鍵值對個數 size),時,就會進行數組擴容。loadFactor 的預設值:DEFAULT_LOAD_FACTOR)為0.75,這是一個折中的取值,也就是說,預設情況下,數組大小(DEFAULT_INITIAL_CAPACITY)為16 ,那麼當 HashMap 中元素個數超過 16 * 0.75 = 12 (這個值就是代碼中的 threshold值,也叫臨界值)的時候,就把數組的大小擴展為 2 * 16 = 32 ,即擴大 1倍 ,然後重新計算每個元素在數組中的位置,而這是一個非常消耗性能的操作。所以在開發中如果我們可以預估計其存儲的數據量,也就是 HashMap中存儲元素的個數,那麼就調用其HashMap(int num) 設定存儲容量的大小,減少擴容次數,提高 HashMap的性能

在這裡插入圖片描述

樹形化

HashMap中的其中一個鏈的對象個數如果達到了8個,此時如果capacity沒有達到64,那麼HashMap會先擴容解決,如果已經達到了64,那麼這個鏈會變成,結點類型由Node變成TreeNode類型。當然,如果當映射關係被移除後,下次resize方法時判斷樹的結點個數低於6個,也會把樹再轉為鏈表

在這裡插入圖片描述

補充:

關於映射關係的key是否可以修改 ???

answer:不要修改,映射關係存儲到 HashMap 中會存儲 key哈希值 ,這樣就不用每次查找時,重新計算每一個 EntryNode (TreeNode)的 哈希值了,因此如果已經 putMap 中的映射關係,再修改 key 的屬性,而這個屬性有參與 hashCode值的計算,那麼會導致匹配不上。

為什麼HashMap擴容時,不是數組滿了的時候擴容而是達到一個的 0.75 的額度才擴容 ???

因為HashMap 集合的底層時由 鏈表 + 數組 + 樹 構成的。由於鏈表的存在,HashMap 當中的數組不一定會存儲滿了。

以及涉及到 HashMap 集合性能最優的效果,散列均勻分佈,所以是到達一定額度 0.75 是最好的情況了.

在這裡插入圖片描述

負載因數值的大小,對HashMap有什麼影響 ???

/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;  // HashMap的預設載入因數

負載因數的大小決定了HashMap的數據密度。

負載因數越大密度越大,發生碰撞的幾率越高,數組中的鏈表越容易長,

造成查詢或插入時的比較次數增多,性能會下降。

負載因數越小,就越容易觸發擴容,數據密度也越小,意味著發生碰撞的

幾率越小,數組中的鏈表也就越短,查詢和插入時比較的次數也越小,性能會更高。但是會浪費一定的內容空間。而且經常擴容也會影響性能,建議初始化預設大一點的空間。

按照其他語言的參考及研究經驗,會考慮將負載因數設置為0.7~0.75,此時平均檢索長度接近於常數。

3.3.5 總結:JDK1.8 相較於之前的變化:

  • JDK8 :HashMap map = new HashMap() ,預設情況下,是不會先創建長度為 16 的 數組的,而是首先調用 map.put() 添加鍵值對的時候創建 長度為 16的數組(類比:單例模式中的餓漢式)。 JDK7 則是預設先創建了一個長度為 16的數組(類比:單例模式中的懶漢式)。

  • JDK8 底層的數組是 Node[ ]JDK7 底層的數組是 Entry[ ]

  • put(Key1,Value1) 添加鍵值對時,JDK7 是添加到鏈表上的頭部(數組上),JDK8 是添加到鏈表的尾部(不在數組上),簡稱:七上八下。

  • jdk7 底層結構只有:數組 + 鏈表jdk8 中底層結構: 數組 + 鏈表 + 紅黑樹

    當數組的某一個索引位置上的元素以鏈表形式存在的數據個數 > 8 且當前數組的長度 > 64時,此時此索引位置上的所有數據改為使用“紅黑樹”存儲。當小於 8 時,有會變成鏈表的形式存儲。

4. Map實現類之二:LinkedHashMap

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

  • LinkedHashMapHashMap 的子類,所以 LinkedHashMap 繼承了 HashMap 的特點:其中的 key 是無序,不可重覆的,其 Key 存儲的元素類必須重寫 eqauls() 和 hashCode() 方法。同樣的 Key 值是存儲在 Set 集合當中的,而Value 則是存儲在 Collection 集合當中的。
  • LinkedHashMap 是在 HashMap 存儲結構的基礎上,使用了一對雙向鏈表來記錄添加元素的順序的,所以你添加元素數據的順序是怎樣的,取元素數據的順序就是怎樣的。
  • LinkedHashSet 類似, LinkedHashMap 可以維護 Map 的迭代順序:迭代順序與 Key-Value 鍵值對的插入順序一致,簡單的說就是:存取順序一樣。
  • LinkedHashMap 是繼承了 HashMap 其常用的方法是一樣的,這裡就不多說明瞭。不同的是HashMap 底層的內部類是 Node ,而LinkedHashMap 底層的內部類是Entry ,該內部類繼承了 HashMap.Node<K,V>

HashMap中的內部類:Node

在這裡插入圖片描述

LinkedHashMap中的內部類:Entry

在這裡插入圖片描述

舉例:

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

public class LinkedHashMapTest {
    public static void main(String[] args) {
        // 創建 LinkedHashMap 集合對象
        LinkedHashMap<String,Integer> linkedHashMap = new LinkedHashMap<String, Integer>();

        // 添加元素數據(鍵值對)
        linkedHashMap.put("lihua",99);
        linkedHashMap.put("zhangsan",89);
        linkedHashMap.put("lisi",79);
        linkedHashMap.put("wangwu",69);

        // 遍歷 LinkedHashMap 集合
        // 獲取到key-value 存儲的 Set Entry 內部類集合對象
        Set<Map.Entry<String, Integer>> entries = linkedHashMap.entrySet();
        // 獲取到該 Set Entry 內部類集合的迭代器
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
        while(iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }

    }
}

在這裡插入圖片描述

5. Map實現類之三:TreeMap

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

  • TreeMap 存儲 Key-Value 鍵值對時,需要根據 key-value 鍵值對進行排序,TreeMap 可以保證所有的 key-value 鍵值對處於有序狀態。

  • TreeSet 底層 就是由 TreeMap 構成的,new TreeSet 底層實際上說就是 new TreeMap 集合存儲數據,向 TreeSet 中添加數據就是向 TreeMap 集合中添加數據。

  • TreeMap 中的 key 存儲的數據類型必須是一致的,不然無法比較判斷,從而排序。

  • TreeMap 的 排序是對 Key 的內容進行排序的,其中的 Key 值內部是由 Set 集合存儲的,無序,不可重覆性,所存儲類必須重寫 equals() 和 hashCode() 方法。因為會自動排序,所以還需要實現排序:兩種方式一種是:

    • 自然排序TreeMap 的所有的 Key 必須實現(實現 java.lang.Comparable的介面,而且所有 的 Key 應該是同一個類的對象(因為不是同一類型無法比較判斷),否則將會拋出 ClasssCastException自然排序,重寫其中的 compareTo()抽象方法) 。在Java當中所有的包裝類和String都實現了該 java.lang.Comparable介面。所以一般要實現該介面的都是自定的類。
    • 定製排序: 創建 TreeMap 時,傳入一個 Comparator 對象,該對象負責對 TreeMap 中的所有 key 進行排序。此時不需要 Map 的 Key 實現 Comparable 介面
    public TreeMap(Comparator<? super K> comparator)v // 構造一個新的,空的樹圖,按照給定的比較器排序。
    
    • 關於自然排序與 定製排序 的詳解內容大家可以移步至:
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • At-rules規則是目前CSS中一種常見的語法規則,它使用一個"@"符號加一個關鍵詞定義,後面跟上語法區塊,如果沒有則以分號結束即可。 這種規則一般用於標識文檔、引入外部樣式、條件判斷等等,本文是對該規則的使用總結。 常用規則 @import @import 主要用於從其他樣式表導入新的樣式規則, ...
  • jwt擴展欄位介紹 為了實現業務的定製化需求和開發人員使用的便利,對kc的JWT進行了擴展,這個擴展欄位在解析JWT之後都可以看到。 jwt的payload { "exp": 1675329802, "iat": 1675329622, "jti": "2a80e925-b9ce-464f-822d ...
  • “ DDD設計的目標是關註領域模型而並非技術來創建更好的軟體,假設開發人員構建了一個SQL,並將它傳遞給基礎設施層中的某個查詢服務然後根據表數據的結構集取出所需信息,最後將這些信息提供給構造函數或者Factory,開發人員在做這一切的時候早已不把模型看做重點了,這個整個過程就變成了數據處理的風格 ”... ...
  • 通常,不同的公司里有著不同的編碼規範,主要是從代碼得準確性、穩定性、可讀性等地方著手制定,以提高團隊成員之間的協作效率,這裡主要是列出一些常見的編碼規範。 ...
  • SpringMVC介紹-01 1.離線文檔 解壓 spring-5.3.8-dist.zip文件。 位置:spring-framework-5.3.8/docs/reference/html/web.html#spring-web 2.SpringMVC基本介紹 2.1SpringMVC特點&概述 ...
  • 1 簡介 我們進行Web API開發的時候,經常會使用Json格式的消息體,而Json格式非常靈活,不同的人會有不同的設計風格和實現,而JSON API提供了一套標準。但它並不提供直接實現。 Katharsis是JSON API的Java實現,使用它可以快速開發出Json based的Web介面,還 ...
  • Python有哪些關鍵字 Python常用的關鍵字 and, del, from, not, while, as, elif, global, or, with, assert, else, if, pass, yield, break, except, import, print, class, ...
  • 前言 前面我們已經學習了Python的基礎語法,瞭解了Python的分支結構,也就是選擇結構、迴圈結構以及函數這些具體的框架,還學習了列表、元組、字典、字元串這些Python中特有的數據結構,還用這些語法完成了一個簡單的名片管理系統。下麵我就將介紹一下Python的一些進階語法規則,為後面更複雜的編 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...