ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

HashMap简单解读

2021-10-05 21:03:38  阅读:122  来源: 互联网

标签:key 链表 下标 HashMap 解读 数组 简单 数据 节点


HashMap简单解读

1.简单入门

HashMap就是用来存储和查询数据的。

存储--put

查询--get

通过key,value的形式存储

public static void main(String[] args){
    Map<String,String> map = new HashMap<>();
    map.put("key1","value1");
    map.put("key2","value2");
    map.put("key3","value3");
    System.out.println(map.get("key2"));//此处打印出value2
}

2.HashMap的技术实现

jdk1.7:数组+链表

jdk1.8:数组+链表+红黑树

2.1简单回顾数组和链表数据结构

数组

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

特点:查询O(1),删除插入O(N)

总结:查询快、插入慢

public static void main(String[] args){
    int arr[] = new int[10];
    int[1] = 1;
    int[0] = 0;
    int[5] = 5;
    int[5] = 50;//数据覆盖
}

链表

概念:一种非连续、非顺序的存储结构。直白点说就是不管数据存储在哪个位置,只要指向下一个引用的数据即可。如果要删除某个节点,直接将节点的数据设置为null

特点:插入、删除O(1),查询O(N)。所有的查询都必须从头节点开始,一直查到匹配的数据结束,或查到尾节点。

总结:插入删除快、查询慢

LinkedList底层也是链表,是双向链表:next下一节点,prev上一节点

public class Node {
    public Node next;//引用节点
    private Object data;//当前节点对应的data数据

    //构造方法作用:创建对象时就赋值
    public Node(Object data) {
        this.data = data;
    }
	
    public static void main(String[] args) {
        Node head = new Node("monkey");//创建一个头节点对象,monkey
        head.next = new Node("张三");
        head.next.next = new Node("刘一");
        
        System.out.println(head.data);//打印出monkey
        System.out.println(head.next.data);//打印出张三
    }
}

2.2如何将数据存储在这些数据结构里

现在已经知道了底层的数据结构,那么该如何存数据呢。这里会用到哈希算法。

哈希算法:

就是把任意长度值(key)通过哈希算法变成固定长度的key(地址),通过这个地址来访问数据结构。

这个固定长度的key永远不会变,可以理解为身份证号,从出生到死亡永远都是一个号。

hashcode值:通过字符串算出它的ascii码,进行取模mod,算出Entry数组中的下标。为什么算出来这个ascii码还需要取模,由于是存放在数组中,一般算出的ascii码数值都比较大,为了存一个数据,开辟很长的一个数组,浪费资源。比如存的数据ascii码是429,那么数组的长度也就是430,0-428的下标都是空着的。

//计算ascii码
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]);
    }
}![](https://www.icode9.com/i/l/?n=20&i=blog/2525050/202110/2525050-20211005203931036-731540514.png)


哈希冲突:当不同的数据(key)通过hashcode算出的hash值相同,就会出现哈希冲突。HashMap是允许存不同的key的

当两个key的hash值相同时,存储时:首先会找到数组对应的下标,查看是否有数据,如果没有就存放,如果有,就在该下标处指向下一个节点存放。查询时:先计算取模算出hash值,找到对应的数组下标,会调用equals方法查看数据内容是否匹配,若匹配查询就结束,否则继续指向下一节点,直至查到结束或没有匹配的数据结束。

存储位置示意图:(举例理解)

存放(put)的顺序:

1.先通过hashcode算出hash值(也就是数组下标),将数据放到该数组下标中。

2.如果该下标位置已经有数据了,调用equals方法比较内容是否想等,若不相等,原来的数据把该下标位置让出来,让新数据放进去,此时新数据就是一个头节点,指向原数据的key。(1.7头插法)

读取(get)的顺序:

1.先通过hashcode算出hash值,找到对应的数组下标位置

2.比较hashcode值是否相等,若相等,再用equals判断key是否相等。必须两者都相等,两个对象才相等

2.3红黑树

为什么要用红黑树,因为当hash值相同时,会不断在同一数组下标位置存储数据,导致在查询时由于相同的hash值数据很多,链表太长,查询效率大大降低。

链表取元素是从头结点一直遍历到对应的结点,这个过程的复杂度是O(N) ,而红黑树基于二叉树的结构,查找元素的复杂度为O(logN)

红黑树存储:牺牲维护左中右,小中大这个数据结构的性能,来提高查询速度

比如要查7,从2开始,7比2大,往右节点比,比4大,往右节点比,比6大再往右节点比,7和7相等,查询完成。

阀值:

链表长度达到8就转成红黑树,当长度降到6就转成普通bin,红黑树需要进行左旋,右旋,变色操作来保持平衡。

为什么退化为链表的阈值是6

上面说到,当链表长度达到阈值8的时候会转为红黑树,但是红黑树退化为链表的阈值却是6,为什么不是小于8就退化呢?比如说7的时候就退化,偏偏要小于或等于6?

主要是一个过渡,避免链表和红黑树之间频繁的转换。如果阈值是7的话,删除一个元素红黑树就必须退化为链表,增加一个元素就必须树化,来回不断的转换结构无疑会降低性能,所以阈值才不设置的那么临界。

标签:key,链表,下标,HashMap,解读,数组,简单,数据,节点
来源: https://www.cnblogs.com/wxy1097046369/p/15369521.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有