主頁 > 後端開發 > Java集合 Map 集合 與 操作集合的工具類: Collections 的詳細說明

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

2023-02-04 07:06:39 後端開發

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

在這里插入圖片描述


每博一文案

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

目錄
  • Java集合 Map 集合 與 操作集合的工具類: Collections 的詳細說明
    • 每博一文案
    • 1. Map介面概述
    • 2. Map介面:常用方法
    • 3. Map實作類之一:HashMap
      • 3.1 HashMap的存盤結構
      • 3.2 HashMap原始碼中的重要常量
      • 3.3 HashMap的存盤結構:JDK 1.8之前 / JDK 1.8之后
        • 3.3.1 JDk 1.8 之前
        • 3.3.2 JDk 1.8 及之后
        • 3.3.3 JDK8 HashMap 集合添加元素的程序
        • 3.3.4 JDK8 HashMap 進行 "擴容"和 "樹形化"
        • 3.3.5 總結:JDK1.8 相較于之前的變化:
    • 4. Map實作類之二:LinkedHashMap
    • 5. Map實作類之三:TreeMap
    • 6. Map實作類之四:Hashtable
    • 7. Map實作類之五:Properties
    • 8. Map 介面下的集合遍歷方式
    • 9. Collections工具類
      • 9.1 Collections常用方法
    • 10. 總結:
    • 11. 最后:

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 = https://www.cnblogs.com/TheMagicalRainbowSea/archive/2023/02/03/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 // 構造一個新的,空的樹圖,按照給定的比較器排序,
    
    • 關于自然排序與 定制排序 的詳解內容大家可以移步至:?????? 比較器: Comparable 與 Comparator 區別_ChinaRainbowSea的博客-CSDN博客
  • TreeMap 判斷兩個 Key 相等的標準:兩個 key 通過 重寫的 compareTo()方法或 compare()方法,回傳0 表示相等,

舉例:

TreeMap 集合中存盤自定義類 Person6 物件,其中的 Key 存盤的類為自定義 Person6 物件,該物件重寫了 equals() 和 hashCode()方法,但是沒有重寫比較器的情況,報例外:java.lang.ClassCastException 型別轉換例外,

將其中的Person6 中 age 年齡,升序排列

在這里插入圖片描述


import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;

public class TreeMapTest {
    public static void main(String[] args) {
        // 創建TreeMap 集合物件,其中 Key 存盤的類為自定義 Person6
        TreeMap<Person6,Integer> treeMap = new TreeMap<Person6,Integer>();

        // 添加元素
        treeMap.put(new Person6("lihua",18),99);
        treeMap.put(new Person6("zhangsan",20),89);
        treeMap.put(new Person6("lisi",25),79);
        treeMap.put(new Person6("wangwu",19),69);

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


    }
}

class Person6 {
    String name;
    int age;

    public Person6() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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



    // 當物件中的 name 和 age 屬性值相同回傳 true,否則回傳 fasle
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person6)) return false;
        Person6 person6 = (Person6) o;
        return getAge() == person6.getAge() &&
                Objects.equals(getName(), person6.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }

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

修正: 對 Key 中所存盤的類,提供比較器,方式一:自然排序,該存盤類實作 java.lang.Comparable介面,并重寫其中的 CompareTo()重寫方法,


import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;

public class TreeMapTest {
    public static void main(String[] args) {
        // 創建TreeMap 集合物件,其中 Key 存盤的類為自定義 Person6
        TreeMap<Person6,Integer> treeMap = new TreeMap<Person6,Integer>();

        // 添加元素
        treeMap.put(new Person6("lihua",18),99);
        treeMap.put(new Person6("zhangsan",20),89);
        treeMap.put(new Person6("lisi",25),79);
        treeMap.put(new Person6("wangwu",19),69);

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


    }
}

class Person6 implements Comparable<Person6>{
    String name;
    int age;

    public Person6() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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



    // 當物件中的 name 和 age 屬性值相同回傳 true,否則回傳 fasle
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person6)) return false;
        Person6 person6 = (Person6) o;
        return getAge() == person6.getAge() &&
                Objects.equals(getName(), person6.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }

    /**
     * 升序的比較規則:
     * this > 引數 ,回傳 > 0
     * this < 引數,回傳 < 0
     * this == 引數,回傳 == 0;
     * 降序反過來:
     * this > 引數 ,回傳 < 0
     * this < 引數,回傳 > 0
     * this == 引數,回傳 == 0;
     */
    @Override
    public int compareTo(Person6 o) {
        // 首先判斷該需要比較的引數是否是同一個實體,同一個實體的物件才能比較
        if(o instanceof Person6) {  // 其實這里我們使用了<Person3 o> 泛型限定了,就不需要判斷了
            Person6 person6 = (Person6) o;  // 是對應的實體向下轉型,
            if(this.age > person6.age) {
                return 1;
            } else if( this.age < person6.age) {
                return -1;
            } else {
                return 0;
            }
        } else {
            // throw 可以替代 return
            throw new RuntimeException("型別不一致");  // 拋出運行時例外
        }
    }

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

在這里插入圖片描述

修改方式二:定制排序 創建 TreeMap 集合物件時,將一個匿名實作Com 的類,作為引數,傳遞給構造器,該匿名實作類定制排序按照你 Perso6 中的 age 年齡降序排列

public TreeMap(Comparator<? super K> comparator); // 構造一個新的,空的樹圖,按照給定的比較器排序,
package blogs.blogs7;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;

public class TreeMapTest {
    public static void main(String[] args) {
        // 創建一個 TreeMap 集合物件,將匿名實作的比較器(定制排序),作為引數,傳遞給構造器
        TreeMap<Person6,Integer> treeMap = new TreeMap<Person6,Integer>(new Comparator<Person6>() {
            @Override
            public int compare(Person6 o1, Person6 o2) {
                // 判斷是否是對應比較的實體,其實這里我們可以不用判斷的,因為使用的泛型限定
                if(o1 instanceof Person6 && o2 instanceof  Person6) {
                    Person6 p1 = (Person6)o1;
                    Person6 p2 = (Person6)o2;  // 向下轉型為對應的實體物件,從而獲取比較屬性

                    if(p1.age > p2.age) {
                        return -1;
                    } else if(p1.age < p2.age) {
                        return 1;
                    } else {
                        return 0;
                    }

                }
                // throw 可以代替 return
                throw new RuntimeException("型別不一致");  // 拋出運行時例外
            }
        });

        // 添加元素
        treeMap.put(new Person6("lihua",18),99);
        treeMap.put(new Person6("zhangsan",20),89);
        treeMap.put(new Person6("lisi",25),79);
        treeMap.put(new Person6("wangwu",19),69);

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

    }
}

class Person6 implements Comparable<Person6>{
    String name;
    int age;

    public Person6() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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



    // 當物件中的 name 和 age 屬性值相同回傳 true,否則回傳 fasle
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person6)) return false;
        Person6 person6 = (Person6) o;
        return getAge() == person6.getAge() &&
                Objects.equals(getName(), person6.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }
    

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

在這里插入圖片描述

6. Map實作類之四:Hashtable

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

  • Hashtable 是個古老的 Map 實作類,JDK1.0 就提供了,不同于 HashMapHashtable 是執行緒安全的,其中的方法基本上都不被 synchronized
  • Hashtable 實作原理和 HashMap 相同,功能相同,底層都使用哈希結構,速度快,很多情況下可以互用,
  • HashtebleHashMap 不同,Hashtable 不允許使用 null 作為 KeyValue 的值,不然報,java.lang.NullPointerException 空指標例外,
  • HashtableHashMap 一樣,Hashtable 也不能保證其中 Key-Value 鍵值對的順序,
  • 同樣的 其中的 Key 值內部是由 Set 集合存盤的,無序,不可重復性,所存盤類必須重寫 equals() 和 hashCode() 方法,
  • Hashtable 判斷兩個 key 相等,兩個 value 相等的標準,與 HashMap 是一樣的,Hashtable和HashMap 一樣,底層都是哈希表的資料結構,Hashtable 的初始容量為 11,默認加載因子是 : 0.75,Hashtable 的擴容: 原容量 * 2 + 1;

HashMap和HashTable的比較:

這里寫圖片描述

舉例

在這里插入圖片描述

在這里插入圖片描述

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

public class HashtableTest {
    public static void main(String[] args) {
        Hashtable<String,Integer> hashtable = new Hashtable<String,Integer>();

        hashtable.put("lihua",1);
        hashtable.put("zhangsan",2);
        hashtable.put("lisi",3);
        hashtable.put("wangwu",4);

        Set<Map.Entry<String, Integer>> entries = hashtable.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

在這里插入圖片描述

7. Map實作類之五:Properties

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

  • Properties 類是 Hashtable 的子類,該物件用于處理讀取屬性檔案,
  • 由于屬性檔案里的 key,value都是字串型別的,所以 Properties 里的 key 和 value 都是字串型別
  • 存取資料時,建議使用 setProperty(String key,String value) 方法 和 getProperty(String key) 方法,
public Object setProperty(String key,String value); // 致電Hashtable方法put , 提供與getProperty方法的并行性 , 強制使用字串的屬性鍵和值, 回傳的值是Hashtable呼叫put的結果,簡單的說:就是向Property 集合中添加鍵值對元素,
public String getProperty(String key); // 通過 key 找到對應的 value值,如果沒有找到回傳 null

舉例:

首先我們先在專案中(注意添加到頂級專案中也就是如下的Test 專案下,不是Test2,或者 day模塊下 ,不然無法讀取到)添加一個屬性檔案(以.properties后綴的組態檔)用于Properties 集合讀取,內容如下:

在這里插入圖片描述

name=Tom
password=abc123
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class PropertiesTest {
    public static void main(String[] args) {
        // Properties 集合常用來處理組態檔:key 和 value 都是String型別
        Properties properties = new Properties();

        try {
            // IO流讀取檔案資訊,需要例外處理
            FileInputStream fileInputStream = new FileInputStream("jdbc.properties"); // 檔案名
            properties.load(fileInputStream);  // 加載流對應的檔案,同樣需要例外處理

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        String name = properties.getProperty("name");
        String password = properties.getProperty("password");  // 根據對應檔案中的 key 值獲取到對應的value值

        System.out.println(name);
        System.out.println(password);


    }
}

在這里插入圖片描述

8. Map 介面下的集合遍歷方式

Map 介面下的集合的遍歷方式:注意:Map 集合中沒有下標可以訪問的,也沒有迭代器可以使用的,

方式一: 普遍使用,二次取值,通過獲取 keySet() 方法獲取到 Map 集合中所有的 key 值,回傳一個 Set 集合,再通過遍歷 Set 集合中存盤的所有的 key ,使用 get(key) 方法獲取到對應的 value值,

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

public class Traverse {

    // Map 集合遍歷方式一:
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        // 1.獲取到該 Map 集合當中的所有 Key 值
        Set<String> keys = map.keySet();

        // 2.遍歷所有的 key 值
        for (String key : keys) {
            // 3. 通過 key 獲取到對應的 value 值
            System.out.println(key + "--->" + map.get(key));
        }
    }
}

在這里插入圖片描述

方式二: 通過使用 entrySet() 方法,回傳一個:Set< Map.Entry<K, V> > 集合物件, 再通過獲取到該 Set<Map.Entry> 集合 的迭代器,通過迭代器遍歷,獲取到Map.Entry中存盤的 key(getKey()), value(getVale()) 方法

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

public class Traverse {

    // Map 集合遍歷方式二:
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        // 1. 獲取到 Set<Map.Entry> 集合物件
        Set<Map.Entry<String, String>> entries = map.entrySet();

        // 2. 獲取到該 Set<Map.Entry> 集合 的迭代器
        Iterator<Map.Entry<String, String>> iterator = entries.iterator();

        // 3. 通過迭代器遍歷,獲取到Map.Entry中存盤的 key,value值
        while(iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }

    }
}

在這里插入圖片描述

方式三: 推薦,尤其是容量大時,因為這是一次性獲取到 Map 中所有的key-value 值后,再取出的,效率高

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

public class Traverse {

    // Map 集合遍歷方式三:
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }
    }
}

在這里插入圖片描述

方式四: 通過Map.values() 回傳一個Collection 集合遍歷所有的value,但不能遍歷key

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

public class Traverse {

    // Map集合遍歷方式四:
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        // 獲取到集合當中所有的 value值
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }
    }
}

在這里插入圖片描述

9. Collections工具類

一個獨立的集合 工具類

在這里插入圖片描述

在這里插入圖片描述

  • Collections 是一個操作 Set,List 和 Map 等集合的工具類,
  • 注意區別:Collection 是一個介面集合,而 Collection s 多了個 s 的是 集合工具類,都是在 java.util. 包下的,
  • Collections 中提供了一系列靜態的方法(工具方法麻,一般都是靜態方法)對集合元素進行排序,查詢和修改等操作,還提供了對集合物件設定不可變,對集合物件實作同步控制(解決執行緒安全問題)等方法,

9.1 Collections常用方法

排序:

  • reverse(List lsit) : 反轉 List 中元素的順序
public static void reverse(List<?> list);  // 反轉 List 中元素的順序
  • shuffle(List list): 對 List 集合元素進行隨機排序
public static void shuffle(List<?> list);  // 對 List 集合元素進行隨機排序
  • sort(List list) : 根據元素的自然順序對指定 List 集合元素按升序排序,注意的是: 排序需要存盤的類有比較器呼叫 自然排序(實作 java.lang.Comparable的介面 / 定制排序 Comparator介面)
public static <T extends Comparable<? super T>> void sort(List<T> list);  // 根據元素的自然順序對指定 List 集合元素按升序排序
  • sort(List list, Comparator c) : 根據指定的 Comparator 產生的順序對 List 集合元素進行排序
public static <T> void sort(List<T> list,Comparator<? super T> c);  // 根據指定的 Comparator 產生的順序對 List 集合元素進行排序
  • swap(List list , int i , int j ) : 將指定 list 集合中的 i 處元素和 j 處元素進行交換,注意是左閉右開的,
public static void swap(List<?> list,int i,int j);  // 將指定 list 集合中的 i 處元素和 j 處元素進行交換

查找,替換

  • max(Collection c) : 根據元素的自然順序,回傳給定集合中的最大元素,
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll);  // 根據元素的自然順序,回傳給定集合中的最大元素
  • max(Collection coll, Comarator comp) : 根據Comparator 指定的順序,回傳給定集合中的最大元素,
public static <T> T max(Collection<? extends T> coll,Comparator<? super T> comp);  // 根據 Comparator 指定的順序,回傳給定集合中的最大元素
  • min(Collection c) : 根據元素的自然順序,回傳給定集合中的最小元素,
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll); // 根據元素的自然順序,回傳給定集合中的最小元素,
  • min(Collection coll, Comarator comp) : 根據Comparator 指定的順序,回傳給定集合中的最小元素,
public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp);  // 根據Comparator 指定的順序,回傳給定集合中的最小元素,
  • replaceAll(List list,T oldVal,T newVal) : 用新值替換List 物件的所有舊值,注意存盤的類需要重寫 equals()方法,才能比較判斷找到對應替換的值,
public static <T> boolean replaceAll(List<T> list,T oldVal,T newVal); // 用新值替換List 物件的所有舊值.
  • frequency(Collection c , Object o) : 回傳指定集合中指定元素的出現次數,注意 :存盤的類需要重寫 equals()方法才能比較判斷查找對應的值的個數,
public static int frequency(Collection<?> c,Object o);  // 回傳指定集合中指定元素的出現次數
  • copy(List dest,List src) : 將 src 集合中的內容復制到 dest 集合當中,
public static <T> void copy(List<? super T> dest,List<? extends T> src); // 將 src 集合中的內容復制到 dest 集合當中

注意該 copy(List dest,List src) 方法,兩個集合物件存盤的資料型別是必須是一樣的,不然無法拷貝添加到 dest 集合當中的,

還有拷貝存盤到的物件 dest 的 size()長度 < 被拷貝的 src 的 size()長度 就會拷貝失敗,報例外: IndexOutOfBoundsException

所以拷貝存盤到的物件 dest 的 size()長度 必須 >= 被拷貝的 src 的 size()長度注意是 size()實際存盤元素資料的長度,不是length()集合的長度

如下原始碼:

在這里插入圖片描述

舉例 : 解決思路如下:

package blogs.blogs7;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class CollectionsTest {
    public static void main(String[] args) {
        List<Integer> src = https://www.cnblogs.com/TheMagicalRainbowSea/archive/2023/02/03/new ArrayList();
        src.add(1);
        src.add(99);
        src.add(-1);
        src.add(66);

        // 創建一個 和 src 集合存盤型別一樣的  Arrays.asList(new Integer[src.size()])陣列,并設定該陣列的大小長度為 src.size()
        // 再使用這個陣列創建拷貝存盤的 desc 集合物件,默認陣列沒有添加資料(這里的是 null)
        // 這樣就 desc 就擁有了一個和 sec集合一樣大小的 size()長度了,
        List desc = new ArrayList();
        desc = Arrays.asList(new Integer[src.size()]);
        for (Integer integer : desc) {
            System.out.println(integer);
        }

        // copy()拷貝
        Collections.copy(desc, src);

        System.out.println("***********  拷貝  ************");
        for (Integer num : desc) {
            System.out.println(num);
        }
    }
}

在這里插入圖片描述

10. 總結:

補充 :各個集合的轉換,可以使用對應的方法,或構造器

	@Test
    public void test2() {
        Set<String> set = new HashSet<>();
        set.add("king");
        set.add("kingsoft");
        set.add("king2");
        set.add("king1");

        // 將Set集合轉換成List集合
        List<String> myList = new ArrayList<>(set);
        for(String s : myList) {
            System.out.println(s);
        }
    }

在這里插入圖片描述

在這里插入圖片描述

  • Map 介面下的集合的特點:集合的key 就是一個 Set 集合存盤的,而 value 值則是被 Collectio介面集合存盤的,
    在Set 集合中放資料,實際上放到了Map集合的key 部分中去了,
  • 注意:Map集合中的 Key 都是存盤在 Set 集合當中的(該集合無序,不可重復),所以Map集合當中的 key 存盤的類必須重寫 equals() 和 hashCode() 方法,不然無法處理 Key 的不可重復特點 ,,但是其中的 value 值是可以存盤重復的資料的,而 value 值則是被 Collection 介面集合存盤的,
  • Map 介面與 Collection 并列存在的,用于保存具有映射關系的資料:key-value 被稱為 鍵值對 ,一個key-value 構成一個 Map.Entry,所有的 Map.Entry 構成的集合是 Set 無序的,不可重復的,
  • 理解 HashMap 中的 put() 添加鍵值對元素資料的原理,以及擴容,和樹化的機制,區別 JDK7 / JDK8 的機制不同點
  • 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 介面

11. 最后:

限于自身水平,其中存在的錯誤,希望大家給予指教,韓信點兵——多多益善,謝謝大家,后會有期,江湖再見!!!
在這里插入圖片描述

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/542934.html

標籤:其他

上一篇:pycharm設定python頭檔案模版

下一篇:python語法進階這一篇就夠了

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more