为什么阿里不推荐使用 keySet() 遍历HashMap?

为什么阿里不推荐使用 keySet() 遍历HashMap?

HashMap相信所有学Java的都一定不会感到陌生,作为一个非常重用且非常实用的Java提供的容器,它在我们的代码里面随处可见。因此遍历操作也是我们经常会使用到的。HashMap的遍历方式现如今有非常多种:

使用迭代器(Iterator)。

使用 keySet() 获取键的集合,然后通过增强的 for 循环遍历键。

使用 entrySet() 获取键值对的集合,然后通过增强的 for 循环遍历键值对。

使用 Java 8+ 的 Lambda 表达式和流。

以上遍历方式的孰优孰劣,在《阿里巴巴开发手册》中写道:
在这里插入图片描述
这里推荐使用的是entrySet进行遍历,在Java8中推荐使用Map.forEach()。给出的理由是遍历次数上的不同。

keySet遍历,需要经过两次遍历。

entrySet遍历,只需要一次遍历。

其中keySet遍历了两次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value。

其中后面一段话很好理解,但是前面这句话却有点绕,为什么转换成了Iterator遍历了一次?

我查阅了各个平台对HashMap的遍历,其中都没有或者原封不动的照搬上句话。

Part2keySet如何遍历了两次
我们首先写一段代码,使用keySet遍历Map。

public class Test {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        map.put("k3", "v3");
        for (String key : map.keySet()) {
            String value = map.get(key);
            System.out.println(key + ":" + value);
        }
    }

}
运行结果显而易见的是

k1:v1
k2:v2
k3:v3

两次遍历,第一次遍历所描述的是转为Iterator对象我们好像没有从代码中看见,我们看到的后面所描述的遍历,也就是遍历map,keySet()所返回的Set集合中的key,然后去HashMap中拿取value的。

Iterator对象呢?如何遍历转换为Iterator对象的呢?

首先我们这种遍历方式大家都应该知道是叫:增强for循环,for-each

这是一种Java的语法糖~。

我们可以通过反编译,或者直接通过Idea在class文件中查看对应的Class文件
在这里插入图片描述

public class Test {
    public Test() {
    }

    public static void main(String[] args) {
        Map<String, String> map = new HashMap();
        map.put("k1", "v1");
        map.put("k2", "v2");
        map.put("k3", "v3");
        Iterator var2 = map.keySet().iterator();

        while(var2.hasNext()) {
            String key = (String)var2.next();
            String value = (String)map.get(key);
            System.out.println(key + ":" + value);
        }

    }
}

和我们编写的是存在差异的,其中我们可以看到其中通过map.keySet().iterator()获取到了我们所需要看见的Iterator对象。

那么它又是怎么转换成的呢?为什么需要遍历呢?我们查看iterator()方法

1iterator()

发现是Set定义的一个接口。返回此集合中元素的迭代器

2HashMap.KeySet#iterator()
我们查看HashMap中keySet类对该方法的实现。

final class KeySet extends AbstractSet<K> {
    public final int size()                 { return size; }
    public final void clear()               { HashMap.this.clear(); }
    public final Iterator<K> iterator()     { return new KeyIterator(); }
    public final boolean contains(Object o) { return containsKey(o); }
    public final boolean remove(Object key) {
        return removeNode(hash(key), key, null, false, true) != null;
    }
    public final Spliterator<K> spliterator() {
        return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    public final void forEach(Consumer<? super K> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        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(e.key);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}

其中的iterator()方法返回的是一个KeyIterator对象,那么究竟是在哪里进行了遍历呢?我们接着往下看去。

final class KeyIterator extends HashIterator
    implements Iterator<K> {
    public final K next() { return nextNode().key; }
}

这个类也很简单:

继承了HashIterator类。

实现了Iterator接口。

一个next()方法。

还是没有看见哪里进行了遍历,那么我们继续查看HashIterator类

4HashMap.HashIterator

abstract class HashIterator {
    Node<K,V> next;        // next entry to return
    Node<K,V> current;     // current entry
    int expectedModCount;  // for fast-fail
    int index;             // current slot

    HashIterator() {
        expectedModCount = modCount;
        Node<K,V>[] t = table;
        current = next = null;
        index = 0;
        if (t != null && size > 0) { // advance to first entry
            do {} while (index < t.length && (next = t[index++]) == null);
        }
    }

    public final boolean hasNext() {
        return next != null;
    }

    final Node<K,V> nextNode() {
        Node<K,V>[] t;
        Node<K,V> e = next;
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (e == null)
            throw new NoSuchElementException();
        if ((next = (current = e).next) == null && (t = table) != null) {
            do {} while (index < t.length && (next = t[index++]) == null);
        }
        return e;
    }

    public final void remove() {
        Node<K,V> p = current;
        if (p == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        current = null;
        K key = p.key;
        removeNode(hash(key), key, null, false, false);
        expectedModCount = modCount;
    }
}

我们可以发现这个构造器中存在了一个do-while循环操作,目的是找到一个第一个不为空的entry。

HashIterator() {
    expectedModCount = modCount;
    Node<K,V>[] t = table;
    current = next = null;
    index = 0;
    if (t != null && size > 0) { // advance to first entry
        do {} while (index < t.length && (next = t[index++]) == null);
    }
}

而KeyIterator是extendHashIterator对象的。这里涉及到了继承的相关概念,大家忘记的可以找相关的文章看看,或者我也可以写一篇~~dog。

例如两个类

public class Father {

    public Father(){
        System.out.println("father");
    }
}
public class Son extends Father{

    public static void main(String[] args) {
        Son son = new Son();
    }
}

创建Son对象的同时,会执行Father构造器。也就会打印出father这句话。

那么这个循环操作就是我们要找的循环操作了。

总结
使用keySet遍历,其实内部是使用了对应的iterator()方法。

iterator()方法是创建了一个KeyIterator对象。

KeyIterator对象extendHashIterator对象。

HashIterator对象的构造方法中,会遍历找到第一个不为空的entry。

keySet->iterator()->KeyIterator->HashIterator

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/152598.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java爬取哔哩哔哩视频(可视化)

链接&#xff1a;我的讲解视频https://www.bilibili.com/video/BV14e411Q7oG/ 本文仅供学术用途 先上图 代码 爬虫核心 import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.gargoylesoftware.htmlunit.*; import org.apache.commons.…

系列二十六、idea安装javap -c

一、概述 javap -c是一个能够将.java文件反编译为.class文件的指令&#xff0c;例如我在idea中编写了一个Car.java文件&#xff0c;我想看看这个类被编译后长什么样的&#xff0c;就可以使用该指令进行查看。 二、配置 2.1、 Java Bytecode Decompiler File>Settings>Pl…

大数据分析与应用实验任务八

大数据分析与应用实验任务八 实验目的 进一步熟悉pyspark程序运行方式&#xff1b;熟练掌握pysaprk RDD基本操作相关的方法、函数。 实验任务 进入pyspark实验环境&#xff0c;在图形界面的pyspark命令行窗口中完成下列任务&#xff1a; 在实验环境中自行选择路径新建以自…

非对口专业测试人,婉拒猎头、放弃6份高薪offer,你敢信?

从非对口的国贸专业&#xff0c;步入测试之路&#xff1b;从红色旅游小城湘潭&#xff0c;迈入国际化都市上海。“明确方向-及时实践-谨慎选择-踏实扎根-计划未来”。她的每一步&#xff0c;都走得格外坚定有力......话不多说&#xff0c;让我们一起来看看这位小姐姐的成长故事…

PyTorch:张量与矩阵

PyTorch 是一个基于 Python 的科学计算包&#xff0c;专门针对深度学习研究&#xff0c;提供了丰富的工具和库。在 PyTorch 中&#xff0c;张量&#xff08;tensor&#xff09;是深度学习的核心数据结构&#xff0c;它可以看作是可以进行自动微分的多维数组。张量不仅可以代表标…

DSVPN简介

定义 动态智能VPN&#xff08;Dynamic Smart Virtual Private Network&#xff09;&#xff0c;简称DSVPN&#xff0c;是一种在Hub-Spoke组网方式下为公网地址动态变化的分支之间建立VPN隧道的解决方案。 目的 越来越多的企业希望建立Hub-Spoke方式的IPSec VPN网络将企业总部…

Linux学习第42天:Linux RS232/485/GPS 驱动实验:天外来客

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 Linux的学习笔记今天更新到了第42天。鉴于国往笔记内容整理中出现的问题&#xff0c;我尽量按照平时学习时笔记的要求进行优化。尽量不再大段大段的贴代码。而是…

解决Tomcat中文乱码

cmd乱码如图&#xff1a; idea中运行Tomcat控制台出现乱码&#xff1a; 解决办法&#xff1a; 找到两个idea的vmoptions配置文件&#xff0c;在文件中追加-Dfile.encodingUTF-8 -Dfile.encodingUTF-8保存退出。 重启idea重新运行Tomcat&#xff1a; maven、tomcat 超级详…

什么是 SSL?SSL/TLS是如何工作的?HTTP和HTTPS有什么区别?

SSL 代表安全套接字层&#xff0c;是指用于加密、保护和验证互联网上之通信的协议。尽管 SSL 在一段时间前已被称为 TLS&#xff08;传输层安全性&#xff09;的更新协议代替&#xff0c;但“SSL”仍是该技术的常用术语。 SSL/TLS 的主要用例是保护客户端和服务器之间的通信安…

解决requests库中session.verify参数失效的问题

在使用requests库进行HTTP请求时&#xff0c;如果在环境变量中设置了’REQUESTS_CA_BUNDLE’&#xff0c;并且在session对象中设置了verify参数为False&#xff0c;那么API请求会使用环境变量中的值而不是session对象中的值。这是因为在requests库中&#xff0c;当session对象中…

Find My婴儿车|苹果Find My技术与婴儿车结合,智能防丢,全球定位

婴儿车是一种为婴儿户外活动提供便利而设计的工具车&#xff0c;是宝宝最喜爱的散步交通工具&#xff0c;更是妈妈带宝宝上街购物时的必须品。随着现在三胎的放开&#xff0c;婴儿车市场已经迎来上升的趋势。 在智能化加持下&#xff0c;防丢功能的加入使得人们日益关心物品的…

深度学习YOLO图像视频足球和人体检测 - python opencv 计算机竞赛

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络4 Yolov5算法5 数据集6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习YOLO图像视频足球和人体检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非…

TEMU要求提交RSL Report 铅镉RSL邻苯项目化学物质检测报告

TEMU要求提交RSL Report 铅镉RSL邻苯项目化学物质检测报告 如果您在亚马逊上销售商品&#xff0c;则必须遵守所有适用的欧盟和地方法律法规&#xff0c;以及适用于这些商品和商品信息的亚马逊政策。要在亚马逊上销售某些商品&#xff0c;( xxdu2016 )您需要向我们提供 REACH 符…

mybatis-plus3.5.3.1 支持不同数据源sql适配

mybatis-plus3.5.3.1 支持不同数据源sql适配 背景 最近公司要求支持国产数据库达梦&#xff0c;人大金仓&#xff0c;高斯等数据库&#xff0c;这些数据库与mysql的语法有一些差异&#xff0c;需要做一些兼容操作。 解决问题 1.不同数据库分页不同 2.支持通过参数控制执行…

Spi机制的必要性

SpringBoot 为啥单独加载类路径下spring.factories文件中的类&#xff1f; SpringBoot 应用运行过程中存在两种类型的类初始化&#xff1a;一部分为已经提前装载到IOC容器中的bean&#xff0c;另一部分则为实时new的bean。 IOC容器中的bean包含&#xff1a;启动类所在包路径下…

Docker的3主3从redis集群配置(扩容和缩容配置)

3主3从redis集群配置 1、关闭防火墙启动docker后台服务 systemctl start docker2、新建6个docker容器redis实例 docker run -d --name redis-node-1 --net host --privilegedtrue -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly …

基于Vue+SpringBoot的城市桥梁道路管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询城市桥梁4.2 新增城市桥梁4.3 编辑城市桥梁4.4 删除城市桥梁4.5 查询单个城市桥梁 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的城市桥梁道路管理系统&#xff0c;支持…

小型机房380V断电报警门磁开关状态检测远程控制RTU

在现代社会中&#xff0c;小型机房起到了至关重要的作用&#xff0c;为各种系统和设备提供稳定的电力供应。然而&#xff0c;由于各种原因&#xff0c;如供电故障、设备故障或非法侵入等&#xff0c;机房的正常运行可能会受到威胁。为了保障机房的安全和可靠性&#xff0c;我们…

了解七大经典排序算法,看这一篇就足够了!!!

✏️✏️✏️好&#xff0c;那么今天给大家分享一下七大经典排序算法&#xff01; 清风的CSDN博客 &#x1f61b;&#x1f61b;&#x1f61b;希望我的文章能对你有所帮助&#xff0c;有不足的地方还请各位看官多多指教&#xff0c;大家一起学习交流&#xff01; 动动你们发财的…

java Could not resolve placeholder

1、参考&#xff1a;https://blog.csdn.net/yu1812531/article/details/123466616 2、配置文件: 3、在application.properties中设置要使用的配置文件