吊打面试官,HashMap 这一篇就够了

news2024/9/28 3:20:41

一、HashMap的简单使用

HashMap集合的创建:

Map<String,String> map = new HashMap<String,String>();

使用put存储数据:

map.put("张三","喜欢打游戏");
map.put("李四","喜欢睡觉");
map.put("王五","喜欢看电视");
map.put("赵六","喜欢洗澡");

使用get获取数据:

System.out.println(map.get("张三"));
System.out.println(map.get("李四"));

通过Map.keySet使用iterator遍历HashMap:

// 通过Map.keySet使用iterator遍历key,然后通过key得到对应的value值
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
    String key = iterator.next();
    String value = map.get(key);
    System.out.println("key = " + key + ", value = " + value);
}

通过Map.entrySet使用iterator遍历HashMap:

// 通过Map.entrySet使用iterator遍历key和value;注意 Set entrySet():返回所有key-value对构成的Set集合
Iterator<Map.Entry<String, String>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
    Map.Entry<String, String> entry = entries.next();
    System.out.println(entry);
}

通过Map.keySet遍历HashMap:

// 通过Map.keySet遍历key,然后通过key得到对应的value值
for (String key : map.keySet()) {
    System.out.println("key = " + key + ", value = " + map.get(key));
}

通过For-Each迭代entries,使用Map.entrySet遍历HashMap:

// 使用For-Each迭代entries,通过Map.entrySet遍历key和value
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());
}

使用lambda表达式ForEach遍历HashMap:

// 使用lambda表达式forEach遍历
map.forEach((k, v) -> System.out.println("key = " + k + ", value = " + v));

二、HashMap的底层原理

HashMap本身就是一个程序/工具类,主要用来存储数据

程序=数据结构+算法

数据结构:数组、链表、红黑树

算法:哈希算法

HashMap的数据结构在1.8之前是“数组+链表”

数组:

采用一段连续的存储单元来存储数据

/**
 * 数组
 */
public class MyArray {
    public static void main(String[] args) {
        Integer integer[] = new Integer[10];
        integer[0] = 0;
        integer[1] = 1;
        integer[9] = 2;
        integer[9] = 100;
        System.out.println(integer[9]);
    }
}

特点:查询o(1),删除o(N)

总结:查询快,插入慢

ArrayList的数据结构就是数组:

链表:

链表是一种物理存储单元上非连续、非顺序的存储结构

/**
 * 链表
 */
public class Node {

    public Node next;
    private Object data;

    public Node(Object data){this.data= data;}

    public static void main(String[] args) {
        Node head = new Node("睡觉");
        head.next = new Node("吃饭");
        head.next.next = new Node("打游戏");
        System.out.println(head.data);
        System.out.println(head.next.data);
        System.out.println(head.next.next.data);
    }
}

特点:插入、删除时间复杂度o(1),查找遍历时间复杂度o(N)

总结:插入快,查找慢

LinkedList的数据结构就是链表:

哈希算法:

/**
 * ascii码
 */
public class AsciiCode {
    public static void main(String[] args) {
        char c[] = "lies".toCharArray();
        for (int i = 0; i < c.length; i++) {
            System.out.println((c[i])+":"+(int)c[i]);
        }
    }
}

“取模”的目的是为了节省空间.

但如果两个字符串的ascii码计算结果相同,再进行取模,则会发生“哈希冲突”。.

因为HashMap的数据结构包含了“链表”,而链表就是解决哈希冲突这一问题的关键。

HashMap对产生哈希冲突的数据会以“链表”的形式来存储它们,其它数据则以“数组”的形式存储。

HashMap的数据结构在1.8之后是“数组+链表+红黑树”

由于“链表”查询速度慢的原因,所以新增了“红黑树”:

如上图,这是一个以“链表”形式存储数据的图示

假如我要查询“004”,“005”,“007”则会从001到007查询7次

上图是一个以“红黑树”形式存储数据的图示

红黑树结构特征:“左中右”对应“小中大”

使用“红黑树”查询“004”,“005”,“007”则只需要要查询4次即可

这里要注意,java1.8之后,HashMap默认的数据结构一开始仍然是“数组+链表”,但是当链表的阈值达到8的时候就会变成“数组+红黑树”

查看HashMap源码,点进putVal方法:

点进642行的常量,发现对应的数值就是8:

再看putVal方法中的treeifyBin方法:

此方法一开始执行的是“链表(执行Node)”,而随着条件的改变就变成了“红黑树(执行TreeNode)”:

这里,你可能会好奇,明明知道了“链表”的查询速度慢,为什么一开始不使用“数组+红黑树”???

这就好比“鱼和熊掌不可兼得”。

因为“红黑树”在插入数据的时候需要做“数据左旋(也可以理解为数据位置交换)”的动作,所以“红黑树”的插入速度慢。

而“链表”它的插入速度快,和红黑树相反,所以才有了“当链表的阈值达到8的时候就会变成“数组+红黑树””这一说法。

不知道“阈值”是什么意思的小伙伴可以去百度一下。

三、手写一个HashMap

Map<K,V>接口:

/**
 * K V
 * @param <K>
 * @param <V>
 */
public interface Map<K,V> {
    V put(K k,V v);
    V get(K k);
    int size();

    interface  Entry<K,V>{
        K getKey();
        V getValue();
    }
}

HashMap<K,V>,实现Map<K,V>接口:

/**
 * 手写实现HashMap
 * @param <K>
 * @param <V>
 */
public class HashMap<K,V> implements Map<K,V> {

    Entry<K,V> [] table =null;
    int size = 0;

    public HashMap() {
        table = new Entry[16];
    }

    /**
     * 1、key进行hash 取模算出index下标
     * 2、数组的对应节点对象 对象是否为空
     * 3、如果为空的话 赋值存储
     * 4、如果不为空的话 冲突 链表存储
     * 5、返回值
     * @param k
     * @param v
     * @return
     */
    @Override
    public V put(K k, V v) {
        //1、key进行hash 取模算出index下标
        int index = hash(k);
        //2、数组的对应节点对象 对象是否为空
        Entry<K, V> entry = table[index];
        if (null == entry){
            //3、如果为空的话 赋值存储
            table[index]=new Entry<>(k,v,index,null);
            size++;
        }else {
            //4、如果不为空的话 冲突 链表存储
            table[index]=new Entry<>(k,v,index,entry);
        }
        //5、返回值
        return table[index].getValue();
    }

    private int hash(K k) {
        int index = k.hashCode()%16;
        return index>=0?index:-index;
    }

    /**
     * key 进行hash index下标
     * 是否为空 如果为空 直接返回null
     * 不为空 k 当前k进行比较
     * 如果相等 直接返回数据
     * 如果不相等 next是否为空
     * 如果为空 直接返回
     * 如果不为空
     * k是否key是否相等 直到相等为止
     * @param k
     * @return
     */
    @Override
    public V get(K k) {
        int index = hash(k);
        Entry<K,V> entry = findValue(table[index],k);
        return entry==null?null:entry.getValue();
    }

    public Entry<K,V> findValue(Entry<K,V> entry,K k){
        if (k.equals(entry.getKey())||k==entry.getKey()){
            return entry;
        }else {
            if (entry.next!=null){
                return findValue(entry.next,k);
            }
        }
        return null;
    }

    @Override
    public int size() {
        return size;
    }

    class Entry<K,V> implements Map.Entry<K, V> {
        K k;V v;int index;Entry<K,V> next;

        public Entry(K k, V v, int index, Entry<K, V> next) {
            this.k = k;
            this.v = v;
            this.index = index;
            this.next = next;
        }

        @Override
        public K getKey() {
            return k;
        }

        @Override
        public V getValue() {
            return v;
        }
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        HashMap<Object, Object> map = new HashMap<>();
        map.put("张三","喜欢打游戏");
        map.put("王五","喜欢看电视");
        System.out.println(map.get("张三"));
        System.out.println(map.get("王五"));
    }
}

测试结果:

喜欢打游戏
喜欢看电视

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电网运行信息检索系统的设计与实现

摘要 电网运行方式管理直接决定了电网企业的经济效益和安全效益,随着我国经济和社会的高速发展&#xff0c;我国电网的覆盖面积、网络节点和电压等级也高速增长。但是,我国当前电网运行方式管理工作水平还相对落后&#xff0c;制约了电网的安全经济效益。本文较为详细的分析了电…

第三章《数组与循环》第2节:多维数组

3.1小节介绍的数组都是把数组元素从逻辑上排成一行,因此它们被称为“一维数组”。如果一个班级内有15个学生,这些学生按照身高又分成3排就坐,其排列形式如图3-2所示: 3-2学生身高排列图 如果程序员希望按照每个学生在班级内的位置来存储他们的身高数据该怎么办呢?一些读者…

基于Java+Spring+Vue+elementUI大学生求职招聘系统详细设计实现

博主介绍&#xff1a;✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取联系&#x1f345;精彩专栏推荐订阅&#x1f447;&#x1f…

【项目设计】自主HTTP服务器

文章目录项目介绍网络协议栈介绍协议分层数据的封装与分用HTTP相关知识介绍HTTP的特点URL格式URI、URL、URNHTTP的协议格式HTTP的请求方法HTTP的状态码HTTP常见的HeaderCGI机制介绍CGI机制的概念CGI机制的实现步骤CGI机制的意义日志编写套接字相关代码编写HTTP服务器主体逻辑HT…

Windows AppData介绍

appdata是什么文件夹?&#xff08;应用程序数据&#xff09; 此文件夹是有关帐户信息、系统桌面、安装文件记录、快速启动文件夹等内容的。appdata下有三个子文件夹local&#xff0c;locallow&#xff0c;loaming。当你解压缩包时如果不指定路径&#xff0c;系统就把压缩包解到…

OpenCV图像处理——停车场车位识别

总目录 图像处理总目录←点击这里 十九、停车场车位识别 19.1、项目说明 唐宇迪老师的——OPENCV项目实战 学习 本项目的目的是设计一个停车场车位识别的系统&#xff0c;能够判断出当前停车场中哪些车位是空的。 任务共包含部分&#xff1a; 对图像预处理 从停车场的监控…

Vue基础之组件通信provide、inject

最近发现竟然还有小伙伴还不清楚provide、inject的用法&#xff0c;是时候普及一下provide、inject了&#xff1b; 常用的组件通信基本是父子组件通过props和emit来进行&#xff0c;一旦层级多了起来&#xff0c;props和emit就不好使了。每级都写props的话&#xff0c;会变得非…

PMP每日一练 | 考试不迷路-11.24(包含敏捷+多选)

11.27PMP考试倒计时 3天 每日5道PMP习题助大家上岸PMP&#xff01; ​题目1-2&#xff1a; ​1.在项目的规划阶段完成以后&#xff0c;但在正式执行之前&#xff0c;项目经理需要就项目目标进行沟通并获得承诺。项目经理下一步应该做什么? ( ) A.与所有相关方召开开工会议…

麻了,别再为难软件测试员了

前言 有不少技术友在测试群里讨论&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了,考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些测试工程师了。 这不&#xff0c;为了帮大家节约时…

深度操作系统20.5发布 deepin 20.5更新内容汇总

深度操作系统&#xff08;deepin&#xff09;是一款致力于为全球用户提供美观易用、安全稳定服务的Linux发行版&#xff0c;同时也一直是排名最高的来自中国团队研发的Linux发行版。深度操作系统20.5升级Stable内核至5.15.24&#xff0c;修复底层漏洞&#xff0c;进一步提升系统…

linux 清理垃圾文件

linux的文件系统比windows的要优秀&#xff0c;不会产生碎片&#xff0c;对于长时间运行的服务器来说尤为重要&#xff0c;而且linux系统本身也不会像windows一样产生大量的垃圾文件。不知道这个说法有没有可信度!至少我们可以确定的是linux系统的文件系统是比较优秀的! linux…

如何建立一个自己的网站?不懂代码搭建自己网站详细教程

搭建自己网站的准备&#xff1a; 1、首先需要注册购买一个域名&#xff0c;比如baidu.com&#xff0c;域名注册可以在阿里云或者其它域名注册平台注册。最常见的.com域名一般也就几十元一年&#xff1b; 域名 2、购买一个服务器&#xff0c;服务器也可以在阿里云或者景安等平…

数据结构之希尔排序

希尔排序 在插入排序的基础上&#xff0c;进行完善的算法 举个例子 如图我们把相距举例为4的两个元素组成一个子表 &#xff08;1和5&#xff0c;2和6&#xff0c;3和7&#xff0c;4和8&#xff09; 对各个子表进行直接插入排序 比如对子表2进行直接插入排序 13插入到38前&…

智慧小镇解决方案-最新全套文件

智慧小镇解决方案-最新全套文件一、建设背景二、建设思路三、建设方案四、获取 - 智慧小镇全套最新解决方案合集一、建设背景 智慧小镇&#xff0c;是指在城镇发展过程中&#xff0c;在城镇基础设施、资源环境、社会民生、经济产业、市政治理等领域中&#xff0c;充分利用物联…

2022亚太杯C题思路代码分析

C题就是数学比较开放的题目了&#xff0c;属于一个数据分析类题目&#xff0c;跟前两年的华为杯差不多&#xff0c;考察的也是全球变暖问题。更多内容文末名片查看 问题1.你同意有关全球气温的说法吗&#xff1f;使用2022_APMCM_C_Data。附件中的csv和其他您的团队收集的数据集…

用DIV+CSS制作四川成都美食网页介绍(网页设计期末课程设计)

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法&#xff0c;如盒子的嵌套、浮动、margin、border、background等属性的使用&#xff0c;外部大盒子设定居中&#xff0c;内部左中右布局&#xff0c;下方横向浮动排列&#xff0c;大学学习的前端知识点和布局方式都有…

RetinaNet-Obj

真是东西多多每天都有新发现 论文地址&#xff1a;https://arxiv.org/abs/1908.09075 样本不平衡问题感觉近期研究的论文相对较多&#xff0c;如&#xff1a;2019 AAAI GHM&#xff0c;2019 CVPR AP-loss&#xff0c; 还有2019 DR loss&#xff0c; 2019 IoU-bala…

国外Essay写作怎么准确高效地审题?

Essay写作的重中之重是什么&#xff1f;毫无疑问肯定是审题审题还是审题&#xff0c;任何形式的Essay写作都以正确地审题为前提。小编就为大家分享如何准确而高效地审题。 What is the top priority of Essays writing?There is no doubt that it must be subject review or t…

python聚类分析如何可视化?

前言 聚类分析是一类将数据所对应的研究进行分类的统计方法。这一类方法的共同特点是&#xff0c;事先不知道类别的个数与结构&#xff1b;进行分析的数据是表明对象之间的相似性或相异性的数据&#xff0c;将这些数据看成对对象“距离”远近的一种度量&#xff0c;将距离近的…

制作一个简单HTML传统端午节日网页(HTML+CSS)7页 带报告

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 传统春节网页设计 | 圣诞节节日发展 | 中秋 | 端午传统节日习俗庆祝 | 地区特色 | 网站模板 | 等网站的设计与制 | HTML期末大学生网页设计作业 HTML&…