HashMap 源码分析06之keySet()、values()、entrySet()、forEach()

1. keySet()、values() 、entrySet()

  • 获取 keys、values 、entrySet集合,keySet、values 是AbstractMap的变量,entrySet是hashMap 的变量,HashMap继承了AbstractMap,所以HashMap三个变量都能使用
  • 源码很简单,变量为null,则new 对应的类型,否则直接返回
/* keySet 为 null 就新建一个 */
public Set<K> keySet() {
    Set<K> ks = keySet;
    if (ks == null) {
        ks = new KeySet();
        keySet = ks;
    }
    return ks;
}
public Collection<V> values() {
    Collection<V> vs = values;
    if (vs == null) {
        vs = new Values();
        values = vs;
    }
    return vs;
}
public Set<Map.Entry<K,V>> entrySet() {
    Set<Map.Entry<K,V>> es;
    return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}

2. key、value、entrySet 的迭代器Iterator与分解迭代器Spliterator

  • 可以回看01篇,有这几个类的源码解析,本章演示为主
import java.util.*;
import java.util.Map.Entry;

public class HashMapTest03 {
    public static void main(String[] args) {
        HashMap<String, Integer> hashMap = new HashMap<>();
        for(int i = 1; i <= 10; i++) {
            hashMap.put("我是" + i, i);
        }
        System.out.println("now hashMap: " + hashMap);
        Set<String> keySet = hashMap.keySet();
        System.out.println("hashMap.keySet(): " + keySet);
        Iterator<String> keyIterator = keySet.iterator();
        if(keyIterator.hasNext()) System.out.println("keyIterator.next(): " + keyIterator.next());
        Spliterator<String> keySpliterator = keySet.spliterator();
        keySpliterator.tryAdvance((e) -> System.out.println("keySpliterator.tryAdvance(): " + e));
        Spliterator<String> keySpliterator01 = keySpliterator.trySplit();
        System.out.println("keySpliterator.estimateSize(): " + keySpliterator.estimateSize());
        System.out.println("keySpliterator01.estimateSize(): " + keySpliterator01.estimateSize());
        Spliterator<String> keySpliterator02 = keySpliterator.trySplit();
        Spliterator<String> keySpliterator03 = keySpliterator01.trySplit();
        System.out.println("keySpliterator.estimateSize(): " + keySpliterator.estimateSize());
        System.out.println("keySpliterator01.estimateSize(): " + keySpliterator01.estimateSize());
        System.out.println("keySpliterator02.estimateSize(): " + keySpliterator02.estimateSize());
        System.out.println("keySpliterator03.estimateSize(): " + keySpliterator03.estimateSize());
        System.out.println("============== 我是一条警戒线 ============");
        /* 这里可以看到,四个 estimateSize() 加起来为8, 少的 1 个去哪了(前面有1次tryAdvance),我们来输出看看 */
        System.out.println("===== keySpliterator中的元素 ===== : ");
        keySpliterator.forEachRemaining(e -> System.out.print(e + "\t"));
        System.out.println();
        System.out.println("===== keySpliterator01中的元素 ===== : ");
        keySpliterator01.forEachRemaining(e -> System.out.print(e + "\t"));
        System.out.println();
        System.out.println("===== keySpliterator02中的元素 =====");
        keySpliterator02.forEachRemaining(e -> System.out.print(e + "\t"));
        System.out.println();
        System.out.println("===== keySpliterator03中的元素 ===== : ");
        /* 这里不输出,为了后面的继续分割,keySpliterator01 已经有三个元素
            keySpliterator03.forEachRemaining(e -> System.out.println(e));
        */
        /* 原因解释:
        * new KeySpliterator<>(map, lo, index = mid, est >>>= 1,  expectedModCount);
        * 重点 est >>>= 1:若 est =  5, est >>>= 1 即 est = 2, 当前 this.est = 2, new.est = 2 真相大白。可能这就是大致个数的原因吧
        *  */

        Spliterator<String> keySpliterator04 = keySpliterator03.trySplit();
        System.out.println("keySpliterator04.estimateSize(): " + keySpliterator04.estimateSize());
        System.out.println("========= 元素太少,继续分割为 null ==========");
        Spliterator<String> keySpliterator05 = keySpliterator04.trySplit();
        System.out.println("keySpliterator05(): " + keySpliterator05);

        System.out.println("========================");
        Collection<Integer> values = hashMap.values();
        System.out.println("hashMap.values(): " + values);
        Iterator<Integer> valuesIterator = values.iterator();
        System.out.println("valuesIterator:  ");
        valuesIterator.forEachRemaining((e) -> System.out.print(e + "\t"));
        System.out.println();
        Spliterator<Integer> valuesSpliterator = values.spliterator();
        System.out.println("valuesSpliterator:  ");
        valuesSpliterator.forEachRemaining((e) -> System.out.print(e + "\t"));
        System.out.println();
        System.out.println("========================");
        Set<Entry<String, Integer>> entrySet = hashMap.entrySet();
        System.out.println("hashMap.entrySet(): " + entrySet);
        Iterator<Entry<String, Integer>> entryIterator = entrySet.iterator();
        System.out.println("entryIterator:  ");
        entryIterator.forEachRemaining(e -> System.out.print(e.getKey() + "=" + e.getValue() + "\t"));
        System.out.println();
        Spliterator<Entry<String, Integer>> entrySpliterator = entrySet.spliterator();
        System.out.println("entrySpliterator:  ");
        entrySpliterator.forEachRemaining(e -> System.out.print(e.getKey() + " = " + e.getValue() + "\t"));
    }
}

  • 输出如下
now hashMap: {我是6=6, 我是7=7, 我是8=8, 我是9=9, 我是2=2, 我是3=3, 我是4=4, 我是5=5, 我是10=10, 我是1=1}
hashMap.keySet(): [我是6, 我是7, 我是8, 我是9, 我是2, 我是3, 我是4, 我是5, 我是10, 我是1]
keyIterator.next(): 我是6
keySpliterator.tryAdvance(): 我是6
keySpliterator.estimateSize(): 5
keySpliterator01.estimateSize(): 5
keySpliterator.estimateSize(): 2
keySpliterator01.estimateSize(): 2
keySpliterator02.estimateSize(): 2
keySpliterator03.estimateSize(): 2
============== 我是一条警戒线 ============
===== keySpliterator中的元素 ===== : 
我是10	我是1	
===== keySpliterator01中的元素 ===== : 
我是9	我是2	我是3	
===== keySpliterator02中的元素 =====
我是4	我是5	
===== keySpliterator03中的元素 ===== : 
keySpliterator04.estimateSize(): 1
========= 元素太少,继续分割为 null ==========
keySpliterator05(): null
========================
hashMap.values(): [6, 7, 8, 9, 2, 3, 4, 5, 10, 1]
valuesIterator:  
6	7	8	9	2	3	4	5	10	1	
valuesSpliterator:  
6	7	8	9	2	3	4	5	10	1	
========================
hashMap.entrySet(): [我是6=6, 我是7=7, 我是8=8, 我是9=9, 我是2=2, 我是3=3, 我是4=4, 我是5=5, 我是10=10, 我是1=1]
entryIterator:  
我是6=6	我是7=7	我是8=8	我是9=9	我是2=2	我是3=3	我是4=4	我是5=5	我是10=10	我是1=1	
entrySpliterator:  
我是6 = 6	我是7 = 7	我是8 = 8	我是9 = 9	我是2 = 2	我是3 = 3	我是4 = 4	我是5 = 5	我是10 = 10	我是1 = 1	
Process finished with exit code 0

3. forEach

  • 遍历HashMap的每一个元素,执行 action 动作
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
    Node<K,V>[] tab;
    /* 执行动作 判断是否为 null */
    if (action == null)
        throw new NullPointerException();
    /* 是否有元素 && tab 不为空 */    
    if (size > 0 && (tab = table) != null) {
        int mc = modCount;
        /* 桶位遍历 */
        for (int i = 0; i < tab.length; ++i) {
        	/* 链条遍历 */
            for (Node<K,V> e = tab[i]; e != null; e = e.next)
            	/* 执行 action.accept, 传入 key,value 值 */
                action.accept(e.key, e.value);
        }
        /* 修改版本,此时所有数据已遍历完毕 */
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }
}
  • 代码测试
import java.util.HashMap;

public class HashMapTest04 {
    public static void main(String[] args) throws InterruptedException {
        HashMap<String, Integer> hashMap = new HashMap<>();
        for(int i = 1; i <= 10; i++) {
            hashMap.put("我是" + i, i);
        }
        /* 不发生修改可以正常遍历 */
        System.out.println("now hashmap: " + "\t");
        hashMap.forEach((k, v) -> {
            System.out.print(k + " = " + v + "\t");
        });
        System.out.println();
        /* 模拟修改的情况 */
        new Thread(() -> {
            try {
                Thread.sleep(300);
                hashMap.put("我是11", 11);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        System.out.println("now hashmap: " + "\t");
        try{
            hashMap.forEach((k, v) -> {
                try {
                    Thread.sleep(100);
                    System.out.print(k + " = " + v + "\t");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        } catch (Exception e) {
            System.out.println();
            e.printStackTrace();
        }
        System.out.println("======== 华丽分割线 =======");
        Thread.sleep(1000);
        /* 试试 forEach 发生删除会怎样 */
        System.out.println("now hashmap: " + "\t");
        try{
            hashMap.forEach((key, value) -> {
                System.out.print(key + " = " + value + "\t");
                hashMap.remove(key);
            });
        } catch (Exception e) {
            System.out.println();
            e.printStackTrace();
        }
        Thread.sleep(1000);
        System.out.println("now hashmap: " + hashMap);
    }
}
  • 对 HashMap 的操作会导致 ConcurrentModificationException 报错,但是在源码也可以看到,是 action 全部执行完才报错。所以若需要在遍历时改变其结构,建议使用 iterator。
    在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值