天天看點

【Java基礎】分析Map接口常用實作類

文章目錄

        • Java集合架構圖
        • Map接口簡介
        • Map接口的基本操作
        • Map接口直接或者間接的實作類
          • HashMap
          • Hashtable
          • LinkedHashMap
          • TreeMap
          • WeakHashMap
          • EnumMap
          • IdentifyHashMap
          • ConcurrentHashMap
        • 四種周遊Map接口的方式

==比較的是位址值,而不是HashCode,是以這裡以後千萬不要掉進誤區了。!!!

Java集合架構圖

點選放大檢視

【Java基礎】分析Map接口常用實作類

List , Set, Map都是接口,List , Set繼承至Collection接口(Collection繼承至Iterable),Map為獨立接口

Map接口簡介

  1. Map不是collection的子接口或者實作類。Map是一個接口。
  2. Map 的 每個

    Entry

    都持有兩個對象,也就是

    一個鍵一個值

    ,Map 值可以相同,但鍵肯定是唯一的
  3. Map 接口最流行的幾個實作類是

    HashMap、LinkedHashMap、Hashtable 和 TreeMap

    (HashMap、TreeMap最常用)

Map接口的基本操作

方法 描述
int size()擷取Map集合大小(即元素數量)
boolean isEmpty() 判斷是否為空
boolean containsKey(Object key) 判斷是否包含某個鍵
boolean containsValue(Object value) 判斷是否包含某個值
V get(Object key) 擷取某個鍵對應的值
V put(K key, V value) 添加鍵值對(K,V)
V remove(Object key) 移除某個鍵對應的鍵值對
void putAll(Map<? extends K, ? extends V> m) 添加另一個Map集合
void clear() 清空所有鍵值對
Set keySet() 擷取鍵的集合
Collection values() 擷取值的集合
Set<Map.Entry<K, V>> entrySet() 擷取鍵值對實體的集合
interface Entry<K,V> Map中的内部接口

Map接口直接或者間接的實作類

map集合 說明
HashMap Map基于

散清單(hash表)

的實作。插入和查詢“鍵值對”的開銷是固定的。可以通過構造器設定

容量capacity

負載因子load factor

,以調整容器的性能。
LinkedHashMap 類似于HashMap,但是疊代周遊它時,取得“鍵值對”的順序是其

插入次序

,或者是最近最少使用(LRU)的次序。

隻比HashMap慢一點

。而在

疊代通路

時反而更快,因為它

使用連結清單維護内部次序

TreeMap 底層是

二叉樹資料結構

線程不同步

,可用于給Map集合中的

鍵進行排序

EnumMap 集合中的所有

key

必須是

同一個枚舉類

的執行個體。當EnumMap建立後,會表現成一個數組array,這種表現方式是緊湊高效的。EnumMap的順序,不是由添加時順序決定,而是由枚舉類内部定義的枚舉字段順序決定。
HashTable HashMap是

Hashtable的輕量級實作

,非線程安全的實作他們都實作了map接口,主要差別是HashMap鍵值可以為空null,效率可以高于Hashtable。
ConcurrentHashMap ConcurrentHashMap通常隻被看做

并發效率更高的Map

,用來

替換

其他線程安全的Map容器,比如Hashtable和Collections.synchronizedMap。
WeakHashMap

弱鍵(weak key)Map

Map中使用的對象也被允許釋放

: 這是為解決特殊問題設計的。如果沒有map之外的引用指向某個“鍵”,則此“鍵”可以被垃圾收集器回收
IdentifyHashMap 使用

==代替equals()對“鍵”作比較的

hash map
ArrayMap(安卓) ArrayMap是一個<key,value>映射的資料結構,它設計上更多的是考慮

記憶體的優化

内部是使用兩個數組進行資料存儲

,一個數組

記錄key的hash值

,另外一個數組

記錄Value值

,它和SparseArray一樣,也會對

key

使用

二分法

進行

從小到大

排序,在添加、删除、查找資料的時候都是

先使用二分查找法

得到相應的

index

,然後通過index來進行添加、查找、删除等操作,是以,應用場景和SparseArray的一樣,如果在

資料量比較大

的情況下,那麼它的性能将退化至少

50%

SparseArray(安卓) SparseArray

比HashMap更省記憶體

,在某些條件下性能更好,主要是因為它

避免

了對

key的自動裝箱

(int轉為Integer類型),它

内部

則是通過

兩個數組

來進行資料

存儲

的,一個

存儲key

,另外一個

存儲value

,為了優化性能,它内部對資料還采取了

壓縮

的方式來表示稀疏數組的資料,進而節約記憶體空間。
HashMap
  • 底層結構
    版本 結構 數組類型 初始化容量
    Java1.6/1.7 位桶(數組) + 連結清單 Entry 16
    Java1.8 位桶(數組) + 連結清單 + 紅黑樹

    (當連結清單長度超過門檻值 “8” 時,将連結清單轉換為紅黑樹

    Node

1.8前使用位桶和連結清單實作

【Java基礎】分析Map接口常用實作類

1.8中HashMap是以數組+連結清單+紅黑樹的模式存儲

(當連結清單長度超過門檻值 “8” 時,将連結清單轉換為紅黑樹)

,當同一個hash值(Table上元素)的連結清單節點數不小于8時,将不再以單連結清單的形式存儲了,它是

線程不安全

的Map,并允許使用

Null值

Null鍵,

方法上都沒有synchronize關鍵字修飾,具體可以參考HashMap1.7和1.8的差別

【Java基礎】分析Map接口常用實作類
Hashtable
  • Hashtable是

    線程安全

    的一個Map,,它在各個方法上添加了

    synchronize關鍵字

    但是現在已經不再推薦使用HashTable了

    ,因為現在有了

    ConcurrentHashMap

    這個專門用于

    多線程場景

    下的map實作類,其大大優化了多線程下的性能。
  • key

    value

    不允許為空,線程安全,效率低
LinkedHashMap
  • LinkedHashMap底層資料結構是

    哈希表和連結清單

    哈希表

    保證鍵

    唯一

    連結清單

    保證

    有序

    ,根據

    插入順序

    存儲
LinkedHashMap<Integer, String> linkedHashMap= new LinkedHashMap<Integer, String>();
		linkedHashMap.put(01, "張三1");
		linkedHashMap.put(02, "張三2");
		linkedHashMap.put(03, "張三3");
		linkedHashMap.put(04, "張三4");
		linkedHashMap.put(05, "張三5");
		
		Set<Integer> keys = linkedHashMap.keySet();
		for (Integer key : keys) {
			System.out.println(key + "|" + linkedHashMap.get(key));
		}
           

執行結果:

【Java基礎】分析Map接口常用實作類
TreeMap
  • 基于

    紅黑樹

    (Red-Black tree)的 NavigableMap 實作。該映射根據其

    鍵的自然順序

    進行排序,或者根據建立時提供的

    Comparator (内比較器)

    進行排序
  • 排序方式類似于TreeSet,分為

    自然排序Comparable

    比較器排序Comparator

    ,具體排序取決于在構造器中使用使用比較器

預設自然排序(自然順序,從小到大)

TreeMap<Integer, String> treeMap= new TreeMap<Integer, String>();
		treeMap.put(24, "Hello1");
		treeMap.put(14, "Hello2");
		treeMap.put(34, "Hello3");
		treeMap.put(124, "Hello4");
		treeMap.put(24, "Hello5");
		treeMap.put(24, "Hello6");
		treeMap.put(24, "Hello7");
		treeMap.put(244, "Hello8");
		treeMap.put(624, "Hello9");
		treeMap.put(24, "Hello10");
		Set<Integer> keys = treeMap.keySet();
		for (Integer key : keys) {
			String value = treeMap.get(key);
			System.out.println(key + "|" + value);
		}
           

執行結果

【Java基礎】分析Map接口常用實作類

比較器排序(使用Comparatpr倒序)

TreeMap<Integer, String> treeMap = new TreeMap<Integer, String>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        treeMap.put(24, "Hello1");
        treeMap.put(14, "Hello2");
        treeMap.put(34, "Hello3");
        treeMap.put(124, "Hello4");
        treeMap.put(24, "Hello5");
        treeMap.put(24, "Hello6");
        treeMap.put(24, "Hello7");
        treeMap.put(244, "Hello8");
        treeMap.put(624, "Hello9");
        treeMap.put(24, "Hello10");
        Set<Integer> keys = treeMap.keySet();
        for (Integer key : keys) {
            String value = treeMap.get(key);
            System.out.println(key + "|" + value);
        }
           

執行結果

【Java基礎】分析Map接口常用實作類
  • TreeMap是NavigableMap接口實作類,NavigableMap接口繼承了SortedMap(繼承了Map)即一個支援排序的map,是以treemap才會支援排序

Treemap四個構造方法

  • 無參 預設實作key的自然排序
  • 有參 參數為comparator比較器 實作定制排序
  • 有參 參數為map 實作key的自然排序
  • 有參 參數為map,comparator比較器 實作key的定制排序

自然排序: 要求待添加元素必須實作compareable接口并實作compareTo方法

自定義排序: 要求待添加元素無序實作compareable接口,但建立Treemap對象時必須傳入comparator的比較器,并實作compare方法(匿名内部類)

WeakHashMap
  • WeakHashMap 以

    弱鍵

    實作的,它是

    Key和Value

    都可以是null,的非同步且無序的散清單(hash哈希表)
  • Map中如果這個Key值指向的對象沒被使用

    (除了自身有對key的引用外,此key沒有其他引用那麼此map會自動丢棄此值,然後被GC回收 )

    ,此時觸發了GC,該對象就會被回收掉的。
  • HashMap的key保留了對實際對象的強引用,這意味着隻要HashMap對象不被銷毀,還HashMap的所有key所引用的對象就不會被垃圾回收,HashMap也不會自動删除這些key所對應的key-value對;
  • WeakHashMap的key隻保留對實際對象的弱引用,這意味着如果WeakHashMap對象的key所引用的對象沒有被其他強引用變量所引用,則這些key所引用的對象可能被垃圾回收,WeakHashMap也可能自動删除這些key所對應的key-value對。
  • WeakHashMap中的每個key對象隻持有對實際對象的弱引用,是以,當垃圾回收了該key所對應的實際對象之後,WeakHashMap會自動删除該key對應的key-value對。
  • 原理主要是使用的

    WeakReference

    ReferenceQueue

    實作的,其key就是

    weakReference

    ,而

    ReferenceQueue

    中儲存了被回收的 Key-Value。
  • 如果當其中一個

    Key-Value

    不再使用被回收時,就将其加入

    ReferenceQueue隊列

    中。當下次再次調用該

    WeakHashMap

    時,就會去更新該map,比如

    ReferenceQueue中的key-value

    ,将其中包含的key-value全部删除掉。這就是所謂的

    “自動删除”

HashMap和WeakHashMap的差別也在于此,HashMap的key是對實際對象的

強引用

  • 弱引用(WeakReference)的特性是:當gc線程發現某個對象隻有弱引用指向它,那麼就會将其銷毀并回收記憶體。WeakReference也會被加入到引用隊列queue中。
WeakHashMap<String,String> whm = new WeakHashMap<>();
        whm.put(new String("hello1"), "world1");
        whm.put(new String("hello2"), "world2");
        whm.put(new String("hello3"), "world3");
        whm.put("hello4", "world3");
        System.out.println(whm);
        // System.gc()來通知JVM進行垃圾回收
        System.gc();
        System.runFinalization();
        System.out.println(whm);
           

執行結果

【Java基礎】分析Map接口常用實作類
EnumMap
  • EnumMap,該類是專門

    針對枚舉類設計

    的一個集合類。建立EnumMap是必須

    指定一個枚舉類

    ,進而将該EnumMap和指定枚舉類關聯起來。
  • 該Map在内部以

    數組

    的形式儲存,是以這種實作形式非常緊湊、高效
  • EnumMap不允許key為空,value可以為空
  • EnumMap的順序,不是由添加時順序決定,而是由枚舉類内部定義的

    枚舉字段順序

    決定。
  • 線程不安全,最好在建立的時候調用

    Collections.synchronizedMap

    方法來進行同步。
class TestEnumMap {
    public static void main(String[] args) {
        //在建立EnumMap時必須顯示或隐式指定它對應的枚舉類
        EnumMap<Direction, String> enumMap = new EnumMap<>(Direction.class);

        // 所有的key都必須是單個枚舉類的枚舉值
        enumMap.put(Direction.UP, "向上移動");
        enumMap.put(Direction.DOWN, "向下移動");
        enumMap.put(Direction.LEFT, "向左移動");
        enumMap.put(Direction.RIGHT, "向右移動");

        //EnumMap根據key的自然順序(枚舉值在枚舉類的定義順序)來維護key-value對的順序
        for (Map.Entry<Direction, String> entry : enumMap.entrySet())
            System.out.println(entry.getKey() + "-----" + entry.getValue());
    }

    /**
     * 内部枚舉類
     */
    enum Direction {
        UP,
        LEFT,
        DOWN,
        RIGHT;
    }
}
           

執行結果:

【Java基礎】分析Map接口常用實作類

插入EnumMap的順序是UP,DOWN,LEFT,RIGHT 但是實際列印順序是 UP,LEFT,DOWN,RIGHT,可以看出 EnumMap的順序是不是由添加時順序決定, 而是有枚舉類中定義的字段順序決定

IdentifyHashMap

IdentityHashMap不是Map的通用實作,它有意違反了Map的正常協定。是一個可以

添加重複key

的Map實作類, 并且

key/value都允許為空

,且是

線程不安全

  1. IdentityHashMap 和 HashMap差別
    • IdentifyHashMap和HashMap差不多,唯一的差別就是在

      底層

      判斷key的方式不一樣

    • HashMap類判斷鍵k1和k2相等的條件為

      p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))

      : 先判斷hashCode是否相同,如果hashCode相同,在用equals判斷值是否相同
    • IdentifyHashMap判斷k1和k2相等的條件是

      (item == k)

      :判斷記憶體位址是否相等

      (上面紅色均為HashMap,IdentityHashMap部分源碼)

  2. 對比HashMap和IdentityHashMap

    添加

    元素差別

    HashMap(java.8)

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }


    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;
    }
           

可以得出結論

if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))

1. 通過新key的hashCode()方法,計算出哈希碼,然後從Node數組中找到對應的位置,若為null就放進去。若已經有值了,請看第二步
2. 調用新key的equals()方法去和已經存在的key比較,如果傳回ture 。則視新鍵與已經存在的鍵相同,用新值去更新舊值,然後put方法傳回舊值
3. 若調用equals()傳回false,則認為新鍵和已存在的鍵不一樣,那就會建立一個Node節點,放在此連結清單裡
           

HashMap的put()方法傳回null的特殊情況:

1. 要是已經存在鍵的映射,但是值是null,那麼調用put()方法再更新鍵的值時, put()方法會把舊值null傳回(因為舊值為null,是以很特殊)
2. 要是找到的位置上沒有鍵的映射,put()方法也是傳回null
           

IdentityHashMap

public V put(K key, V value) {
        final Object k = maskNull(key);

        retryAfterResize: for (;;) {
            final Object[] tab = table;
            final int len = tab.length;
            int i = hash(k, len);

            for (Object item; (item = tab[i]) != null;
                 i = nextKeyIndex(i, len)) {
                if (item == k) {
                    @SuppressWarnings("unchecked")
                        V oldValue = (V) tab[i + 1];
                    tab[i + 1] = value;
                    return oldValue;
                }
            }

            final int s = size + 1;
            // Use optimized form of 3 * s.
            // Next capacity is len, 2 * current capacity.
            if (s + (s << 1) > len && resize(len))
                continue retryAfterResize;

            modCount++;
            tab[i] = k;
            tab[i + 1] = value;
            size = s;
            return null;
        }
    }
           

可以得出結論

if (item == k) {}

IdentityHashMap比較key值,直接使用的是==,隻要兩個對象的記憶體位址相同即會覆寫舊的key/value

示例代碼

測試對象Demo,用于給HashMap以及IdentityHashMap做key

public class Demo {
        private Integer num;
        
        public Demo(Integer num) {
            this.num = num;
        }

        /**
         * 重寫了equals方法,隻要值相同就可以認為是同一個對象
         * @param obj
         * @return
         */
        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (obj instanceof Demo) {
                Demo demo = (Demo) obj;
                if (num.equals(demo.num)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * 重寫hashCode方法,傳回目前num值
         * @return
         */
        @Override
        public int hashCode() {
            return num;
        }
    }
           

測試HashMap 添加元素

@Test
    public void testHashMap() {
        // HashMap put方法去重
        //通過新key的hashCode()方法,計算出哈希碼,然後從Node數組中找到對應的位置,若為null就放進去。若已經有值了,請看第二步
        //調用新key的equals()方法去和已經存在的key比較,如果傳回ture 。則視新鍵與已經存在的鍵相同,用新值去更新舊值,然後put方法傳回舊值
        //調用equals()傳回false,則認為新鍵和已存在的鍵不一樣,那就會建立一個Node節點,放在此連結清單裡
        Map<String, String> testMap1 = new HashMap<>();
        testMap1.put("a", "1");
        testMap1.put("a", "2");
        testMap1.put("a", "3");
        System.out.println(testMap1.size()); //長度1

        Map<String, String> testMap2 = new HashMap<>();
        //String 底層重寫了hashCode/equals方法,所有值相同的對象都會傳回相同的HashCode并且equals傳回true
        testMap2.put(new String("a"), "1");
        testMap2.put(new String("a"), "2");
        testMap2.put(new String("a"), "3");
        System.out.println(testMap2.size()); //長度1

        // Integer 底層重寫了hashCode/equals方法,所有值相同的對象都會傳回相同的HashCode并且equals傳回true
        Map<Integer, String> testMap3 = new HashMap<>();
        testMap3.put(new Integer(200), "1");
        testMap3.put(new Integer(200), "2");
        testMap3.put(new Integer(200), "3");
        System.out.println(testMap3.size()); //長度1

        //Demo類 重寫了hashCode/equals方法,所有值相同的對象都會傳回相同的HashCode并且equals傳回true
        Demo demo1 = new Demo(1);
        Demo demo2 = new Demo(1);
        Demo demo3 = new Demo(1);
        Map<Demo, String> testMap4 = new HashMap<>();
        testMap4.put(demo1, "1");
        testMap4.put(demo2, "2");
        testMap4.put(demo3, "3");
        System.out.println(testMap4.size()); //長度1
    }
           

執行結果

【Java基礎】分析Map接口常用實作類

測試IdentityHashMap 添加元素

@Test
    public void testIdentityHashMap() {
        // IdentityHashMap put方法去重   使用 == 判斷兩個key使用相同, 如果記憶體位址相同覆寫舊key/value

        // 常量 "a" 第一次使用時如果常量池中沒有則會添加一個,後續如果在使用會直接使用常量池中已有的變量,是以記憶體位址是指向同一處地方
        Map<String, String> testMap1 = new IdentityHashMap<>();
        testMap1.put("a", "1");
        testMap1.put("a", "2");
        testMap1.put("a", "3");
        System.out.println(testMap1.size()); //長度1

        // 使用 new String() 會在堆中建立一個對象,然後如果常量池沒有會在常量池中建立 "a" ,是以記憶體位址是指向不是同一處地方
        Map<String, String> testMap2 = new IdentityHashMap<>();
        testMap2.put(new String("a"), "1");
        testMap2.put(new String("a"), "2");
        testMap2.put(new String("a"), "3");
        System.out.println(testMap2.size()); //3

        //,new Integer()會直接在堆中建立對象,并将引用指派給棧中,是以記憶體位址是指向不是同一處地方
        Map<Integer, String> hashMap2 = new IdentityHashMap<>();
        hashMap2.put(new Integer(200), "1");
        hashMap2.put(new Integer(200), "2");
        hashMap2.put(new Integer(200), "3");
        System.out.println(hashMap2.size()); //長度3

        // IdentityHashMap底層使用 == 判斷新key與舊key記憶體位址是否一緻,隻有記憶體位址一緻才會覆寫舊key/value
        Demo demo1 = new Demo(1);
        Demo demo2 = new Demo(1);
        Demo demo3 = new Demo(1);
        Map<Demo, String> testMap3 = new IdentityHashMap<>();
        testMap3.put(demo1, "1");
        testMap3.put(demo1, "1");
        testMap3.put(demo2, "2");
        testMap3.put(demo2, "2");
        testMap3.put(demo3, "3");
        testMap3.put(demo3, "3");
        System.out.println(testMap3.size()); //長度3
    }
           

執行結果:

【Java基礎】分析Map接口常用實作類

注意:

  • IdentityHashMap重寫了equals和hashcode方法,不過需要注意的是hashCode方法并不是借助Object的hashCode來實作的,而是通過

    System.identityHashCode

    方法來實作的。
  • 該類不是線程安全的,如果要使之線程安全,可以調用

    Collections.synchronizedMap(new IdentityHashMap(…))

    方法來實作。
ConcurrentHashMap
  1. Hashtable之是以效率低下主要是因為其實作使用了

    synchronized

    關鍵字對

    put

    等操作進行加鎖,而synchronized關鍵字加鎖是對

    整個對象

    進行加鎖,也就是說在進行put等修改Hash表的操作時,

    鎖住了整個Hash表

    ,進而使得其表現的效率低下;是以,在

    Java1.5~1.7

    版本,Java使用了

    分段鎖

    機制實作ConcurrentHashMap.
  2. 簡而言之,ConcurrentHashMap在對象中儲存了一個

    Segment數組

    ,預設長度為16。即将整個

    Hash表

    劃分為

    多個分段

    ;而

    每個

    Segment元素,即每個分段則類似于一個

    Hashtable

    ;這樣,在執行put操作時首先根據

    hash算法定位

    到元素

    屬于

    哪個

    Segment

    ,然後

    對該Segment加鎖

    即可。是以,ConcurrentHashMap在多線程并發程式設計中可是實作多線程put操作。

    (segment有多少個,理論上就可以同時有多少個線程來持有它這個資源。)

  3. 在JDK1.7之前,ConcurrentHashMap是通過分段鎖機制來實作的,是以其最大并發度受

    Segment的個數限制

    。是以,在JDK1.8中,ConcurrentHashMap取消了

    Segment分段鎖字段

    ,底層采用與HashMap類似的數組+連結清單+紅黑樹的方式實作,而加鎖則采用CAS和synchronized實作。

    (這裡注意Node其實就是儲存一個鍵值對的最基本的對象。其中

    Value和next

    都是使用的

    volatile

    關鍵字進行了修飾,以確定線程安全。)

四種周遊Map接口的方式

  • Entry
    • 由于Map中存放的元素均為

      鍵值對

      ,故每一個鍵值對必然存在一個映射關系。
    • Map中采用

      Entry内部類

      來表示一個鍵值對,鍵值對包含Key和Value (我們總說鍵值對鍵值對, 每一個鍵值對就是一個

      Entry

      )
    • Map.Entry裡面包含getKey()和getValue()方法
Iterator<Map.Entry<Integer, Integer>> it=map.entrySet().iterator();
    while(it.hasNext()) {
        Map.Entry<Integer,Integer> entry=it.next();
        int key=entry.getKey();
        int value=entry.getValue();
        System.out.println(key+" "+value);
    }
           
  • entrySet
    • entrySet是 java中 鍵-值 對的集合,Set裡面的類型是Map.Entry,一般可以通過map.entrySet()得到。
    • entrySet實作了Set接口,裡面存放的是鍵值對。一個K對應一個V。
Set<Map.Entry<String, String>> entryseSet=map.entrySet();

for (Map.Entry<String, String> entry:entryseSet) {
    System.out.println(entry.getKey()+","+entry.getValue());
}
           
  • keySet
    • keySet是鍵的集合,Set裡面的類型即key的類型
Set<String> set = map.keySet();
for (String s:set) {
    System.out.println(s+","+map.get(s));
}
           
  • 四種周遊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");
  
 
    //第一種:鍵找值方式周遊,普遍使用,二次取值
    System.out.println("通過Map.keySet周遊key和value:");
    for (String key : map.keySet()) {
        System.out.println("key= "+ key + " and value= " + map.get(key));
    }
  
     //第二種:周遊entrySet疊代器周遊方式
    System.out.println("通過Map.entrySet使用iterator周遊key和value:");
    Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<String, String> entry = it.next();
        System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
    }
  
    //第三種:周遊entrySet方式,推薦,尤其是容量大時
    System.out.println("通過Map.entrySet周遊key和value");
    for (Map.Entry<String, String> entry : map.entrySet()) {
        System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
    }
 
    //第四種:keySet疊代器周遊
    Iterator<String> iterator=map.keySet().iterator();
      System.out.println("通過Map.keySet使用iterator周遊key和value:");
    while (iterator.hasNext()) {
            String key=iterator.next();
            String  value=map.get(key);
            System.out.println("key: " + key +",value: "+value);
   	}
 }
           

推薦使用第三種方式周遊map集合