Map的遍历方式(源码分析 + 迭代器和CAS思想的关系)

发布于:2025-07-05 ⋅ 阅读:(22) ⋅ 点赞:(0)

各位看官,大家早安午安晚安呀~~~

如果您觉得这篇文章对您有帮助的话

欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦

今天我们来学习:Map的遍历方式(源码分析 + 迭代器和CAS思想的关系)

目录

1.源码提供的,我们要重写BiConsume 的方法:

2.迭代器(安全remove???)

2.1.显示迭代器(安全remove)

迭代器的工作流程:(本质就是把一个容器的元素,全部放到迭代器里面)

2.2.直接foreach循环

2.3. 本质区别

为什么迭代器可以安全删除元素?

关键变量:

安全删除的原理:

3.keySet和values的遍历方式

4.流的方式:

5.比较两种remove:

最后--所有的代码博主双手奉上:

需要查询源码的类


再开始之前,对于Map有困惑的小伙伴可以去看博主关于Map的讲解

博主的Map见解

这里面有一个很重要的点,安全的删除(remove)操作涉及迭代器(HashIterator)的快速失败机制

迭代器的安全删除,两个关键变量

1.expectedModCount 和 modCount 的设计与 CASCompare-And-Swap)理论 有相似之处,它们都基于 乐观锁 的思想。
 

当通过迭代器的remove()方法删除元素时,迭代器会同步更新expectedModCount(将modCount重新赋值给expectedModCount),确保后续操作不会触发异常。例如:
 

iterator.remove(); // 内部会执行 expectedModCount = modCount;

2.可以说,迭代器的快速失败机制是简化版的 CAS它放弃了修复冲突的能力(因为集合结构修改后难以安全恢复),但保留了冲突检测的核心思想。

Map的遍历推荐方式:

推荐1.直接foreach方法

 map.forEach((s,s1) ->{
           System.out.println("key: "+ s + " , value" + s1);
        });

推荐2:隐式迭代器

        for(Map.Entry<String,String> entry: map.entrySet()){
           System.out.println("key :" + entry.getKey() + ",value: "+entry.getValue());
            //TODO,这里不能remove不然会报错呢
            //map.remove(entry.getKey(),entry.getValue());
        }

推荐3:需要删除元素时候(显式迭代器)(下面会讲解)

推荐四:流(下面会讲解)

1.源码提供的,我们要重写BiConsume 的方法:

K,V都提供了。我们直接处理每一组KV即可

重写=》》》(三种写法)

方法引用的写法需要写一个额外的方法:sout(key + value)这样的方法不存在(不要忘了::的前提是一个使用一个已经存在的方法)

    public static void printKV(String key, String value) {
        System.out.println("Key: " + key + ", Value: " + value);
    }

2.迭代器(安全remove???)

2.1.显示迭代器(安全remove)

迭代器的工作流程:(本质就是把一个容器的元素,全部放到迭代器里面)

  1. 获取迭代器:通过 map.entrySet().iterator() 或 map.keySet().iterator() 等方法获取迭代器对象。
  2. 遍历元素:通过 hasNext() 和 next() 方法逐个访问元素。
  3. 安全修改:在遍历过程中,可以通过 iterator.remove() 安全地删除元素。

不可以使用  map.remove(entry);

2.2.直接foreach循环

看一下代码:

结果 =》》》

2.3. 本质区别

为什么迭代器可以安全删除元素?

ransient 是一个关键字,用于修饰类的成员变量。当一个变量被声明为 transient 时,它的核心作用是:在对象序列化时,忽略该变量,不将其状态保存到序列化流中

expectedModCount迭代器内部维护的预期修改计数,初始化时与 modCount 同步

modCount(Map内部维护的修改计数器,每次增删元素时自增)

1.expectedModCount 和 modCount 的设计与 CASCompare-And-Swap)理论 有相似之处,它们都基于 乐观锁 的思想。

2.可以说,迭代器的快速失败机制是简化版的 CAS,它放弃了修复冲突的能力(因为集合结构修改后难以安全恢复)但保留了冲突检测的核心思想

这涉及到 HashMap 的 快速失败(Fail-Fast)机制

搜源码的时候可以 直接搜 HashIterator 这个抽象类下面的remove方法

关键变量:
  • modCountHashMap 内部维护的修改计数器,每次增删元素时自增。
  • expectedModCount迭代器内部维护的预期修改计数,初始化时与 modCount 同步
安全删除的原理:
  1. 调用 iterator.remove()

    • 删除当前元素。(本质上是调用map的remove,然后更新expectModCount使得和 ModCount相等)
    • 更新 modCount 和 expectedModCount,确保两者一致。
  2. 直接调用 map.remove(key) 这个时候仅更新 modCount,但 expectedModCount 不变。下次调用 iterator.next() 时,检测到 modCount != expectedModCount,抛出 ConcurrentModificationException

3.keySet和values的遍历方式

4.流的方式:

并行流处理大量数据效率更高

// 串行流
map.entrySet().stream().forEach(entry -> {
    System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
});

// 并行流(在处理大量数据时效率更高)
map.entrySet().parallelStream().forEach(entry -> {
    System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
});

5.比较两种remove:

1.remove(key)最常用的删除方式

2.remove(key,value)

源码:

方法 参数类型 返回值 描述
remove(Object key) 键(Key) 被删除的值(可能为null 删除指定键对应的键值对,返回被删除的值。若键不存在,返回null
remove(Object key, Object value) 键(Key)和值(Value) 布尔值(是否删除成功) 仅当键值都匹配才删除,返回是否删除成功。Java 8+ 新增。
  1. 日常删除优先使用 remove(key)
  2. 需要验证值是否匹配时使用 remove(key, value)
  3. 迭代过程中删除元素必须使用 iterator.remove()
  4. 多线程环境下使用 ConcurrentHashMap 并调用其 remove(key, value) 方法

最后--所有的代码博主双手奉上:
 

package com.example.demomvc.controller;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.BiConsumer;

public class Test {
    // 定义一个静态方法

    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("第一","111");
        map.put("第二","222");
        map.put("第三","333");


        // map直接的遍历方式 ;匿名内部类,lamda,:: 都可以
        // 1.1.第一种写法
        map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String s, String s2) {
                System.out.println("key :" +s +", value:"+s2);
            }
        });
        // 1.2.第二种写法
        map.forEach((s,s2) -> System.out.println("key :" +s +", value:"+s2));
        // 1.3.第三种写法,第三种写法的注意事项
        // map.forEach(System.out::println); 这种方法不行
        // 方法引用只能引用已经存在了的方法,对于双参数还是要用lambda表达式,而不是方法引用。(或者我们再写一个方法)
        // 你可能尝试过直接用 System.out::println,但它只能处理单个参数(Consumer)
        map.forEach(Test::printKV);

   

//
//        Iterator<Map.Entry<String, String>> iterator1 = map.entrySet().iterator();
//
//        while(iterator1.hasNext()){
//            Map.Entry<String, String> entry = iterator1.next();
//            System.out.println("key:" + entry.getKey() + ", value:" + entry.getValue());
//            if (entry.getValue() == "111") {
//                map.remove(entry); // 这里不可以,会报错
//            }
//        }


        // 迭代器方式1 :生成一个迭代器容器,可以访问迭代器对象,然后使用迭代器对象的方法
        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println("key:" + key + ", value:" + value);
            // 若要在遍历过程中删除元素,可使用iterator.remove()
            if (value == "111") { //删除
                iterator.remove();  // 安全删除
            }
        }
        System.out.println("================");
        // 迭代器方式2, =>>>   这个时候就不会有aaa这个key了
        for (Map.Entry<String,String> entry:map.entrySet()) {
            System.out.println(entry.getKey());
        }



        //使用 keyset 方式
        for(String key:map.keySet()){
            System.out.println("key的值:" + key );
        }

        // 使用 values 迭代方式
        for(String value:map.values()){
            System.out.println("value的值:" + value );
        }


    }

    public static void printKV(String key, String value) {
        System.out.println("Key: " + key + ", Value: " + value);
    }

}

需要查询源码的类

上述就是Map的遍历方式(源码分析 + 迭代器和CAS思想的关系)的全部内容啦,不知道您对文章中的问题和思想是否都学会理解了呢?

能看到这里相信您一定对小编的文章有了一定的认可。

有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正~~

您的支持就是我最大的动力​​​!!!


网站公告

今日签到

点亮在社区的每一天
去签到