HashMap介绍

【重磅更新】CT/MRI LI-RADS 2024:肝细胞癌局部区域治疗后反应评估的核心突破与临床实践指南 减少过度治疗:辐射 LRT 的 Nonprogressing 类别避免了 “持续强化 = 存活” 的误判,降低不必要的再治疗风险;提高检出效率:非辐射 LRT 的辅助特征升级减少了 Equivocal 比例,更早识别存活肿瘤;统一评估语言:标准化的分类标准为放射科、肝病科、肿瘤科、肝移植科的多学科协作(MDT)提供了统一 “沟通工具”,助力治疗方案的精准制定。 阅读详情


1.key-value键值对形式存储;
2.在HashMap中键不能重复,即只能有一个key存在,如果key值相同,value值会被覆盖。
3.key值和value值都可以为null。
4.数据不能保证一致性。

HashMap的属性

  static final int DEFAULT_INITIAL_CAPACITY = 16;//默认容量
 
  static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量是2的30次幂,超过这个容量用之替换
     
  static final float DEFAULT_LOAD_FACTOR = 0.75f;//加载因子是哈希表在其容量自动增加之前可以达到多满的一种程度,默认加载因子0.75
     
  transient Entry<k,v>[] table;//Entry数组保存键值对
  
  transient int size;      //键值对的数量
  
   int threshold;      // 扩容阈值:容量×加载因子得到

HashMap继承关系

public class HashMap<K,V>
         extends AbstractMap<K,V> 
         implements Map<K,V>, Cloneable, Serializable

底层数据结构

HashMap是一个线性数组实现的,里面有一个静态内部类Entry,
属性有key,value,next;所以数组就是Entry[],Map的内容都保存在Entry[]里面。 实则就是数组加链表组成。

构造函数

HashMap有4个构造器,其他构造器如果用户没有传入initialCapacity 和loadFactor这两个参数,会使用默认值

initialCapacity默认为16,loadFactory默认为0.75

public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); 
 }
 
 public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
 }
 
 public HashMap(int initialCapacity, float loadFactor) {
   	 // HashMap的最大容量只能是MAXIMUM_CAPACITY
   	 if (initialCapacity > MAXIMUM_CAPACITY)
   	 initialCapacity = MAXIMUM_CAPACITY;
   	 // 设置 加载因子
   	 this.loadFactor = loadFactor;
   	 threshold = initialCapacity;   
   	 init();
}

 public HashMap(Map<? extends K, ? extends V> m) {
    // 设置容量大小 和 加载因子 
    this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
            DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
    inflateTable(threshold);
    putAllForCreate(m);
}

inflateTable初始化table

private void inflateTable(int toSize) {
        // Find a power of 2 >= toSize
        //找到小于或等于tosize的一个2的幂次方数。
        int capacity = roundUpToPowerOf2(toSize);

        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        table = new Entry[capacity];
        initHashSeedAsNeeded(capacity);
}

private static int roundUpToPowerOf2(int number) {
        // assert number >= 0 : "number must be non-negative";
        return number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}

//将传入的数字i, 找到小于或等于i的一个2的幂次方数。  
public static int highestOneBit(int i) {  i=18  > 2^4  return 4;
    // HD, Figure 3-1  
    i |= (i >> 1);     -(1)
    i |= (i >> 2);    -(2)
    i |= (i >> 4);    -(3)
    i |= (i >> 8);     -(4)
    i |= (i >> 16);     -(5)
    return i - (i >>> 1);      -(6)
}
以二进制的形式进行运算
例:i=18
18 = 0001 0010
(1)  0001 0010右移一位:  0000 1001
     0001 0010 或 0000 1001 :  0001 1011
(2) 0001 1011 右移2位: 0000 0110 
     0001 1011 或 0000 0110 :0001 1111
(3)0001 1111 右移4位: 0000 0001
     0001 1111 或 0000 0000 : 0001 1111
(4)0001 1111 右移8位:0000 0000
     0001 1111 或 0000 0000 :0001 1111
(5)0001 1111 右移8位:0000 0000
     0001 1111 或 0000 0000 :0001 1111
(6)0001 1111 无符号右移1位:0000 1111
    0001 1111 - 0000 1111 :0001 0000= 16=2^4
右移与或运算的目的就是想让某个数字的低位都变为1,再用该结果 减去 该结果右移一位后的结果,则相当于清零了原数字的低位。即得到了想要的结果。

put方法

1.如果table数组为null,首先进行数组初始化
2.利用index=(n-1)&hash的方式,找到索引位置
3.用key进行hash = hash(key)
4.如果索引位置无元素,则创建Node对象,存入数组该位置中
5.如果索引位置已有元素,说明hash冲突
6.若hash值和key值都一样,则进行value值的替代
7.hash值一致,key值不一致,且p为单链表结构,则往单链表中添加 ,追加到单链表末尾

  public V put(K key, V value)
        //如果table数组为null,首先进行数组初始化
        if (table == EMPTY_TABLE) { 
            inflateTable(threshold); 
       	}  
       	//如果key为null,调用putForNullKey方法将key放到数组0号下标位置
        if (key == null)
            return putForNullKey(value);
        //利用index=(n-1)&hash的方式,找到索引位置
        //用key进行hash= hash(key)
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
             Object k;
             //若hash值和key值都一样,则进行value值的替代
             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                  V oldValue = e.value;
                  e.value = value;
                  e.recordAccess(this);
                  return oldValue; //并返回旧的value
             }
         }
         modCount++;
         //hash值一致,key值不一致,,则往单链表中添加 ,追加到单链表末尾
         addEntry(hash, key, value, i); 
         return null;
  }
  ------------------------------------------------------------------------------------
  //和put相关的方法
  void addEntry(int hash, K key, V value, int bucketIndex) {
        //如果size大于thresold(capacity*loadfactory) && 该位置下标不为空,进行数组的扩容
        if ((size >= threshold) && (null != table[bucketIndex])) {
             //2倍扩容
             resize(2 * table.length);
             //重新hash
             hash = (null != key) ? hash(key) : 0;
             bucketIndex = indexFor(hash, table.length);
        }
        //创建节点
        createEntry(hash, key, value, bucketIndex);
  }

  void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
  }
  
  private V putForNullKey(V value) {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
             if (e.key == null) {
                  V oldValue = e.value;
                  e.value = value;
                  e.recordAccess(this);
                  return oldValue;
             }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
  }
  //通过hash和table.length计算存储table下标
  static int indexFor(int h, int length) {
        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
        return h & (length-1);
  }

get方法

先对key进行hash,找到key存在的数组table的索引位置
对该位置链表进行遍历,找到判断key是否相等,(【hashcode】 【==】 【.equals()】 三种方法)

  public V get(Object key) {  
      // 1. 当key == null时,则到 以哈希表数组中的第1个元素(即table[0])为头结点的链表去寻找对应 key == null的键
      if (key == null)  
          return getForNullKey();

      // 2. 当key ≠ null时,去获得对应值 
      Entry<K,V> entry = getEntry(key);

      return null == entry ? null : entry.getValue();  
  }  
 //获取key值为null的value值
   private V getForNullKey() {
      //如果数据个数等于0,则返回null
      if (size == 0) {
          return null;
      }
      //获取key值为null的value
      for (Entry<K,V> e = table[0]; e != null; e = e.next) {
          if (e.key == null)
              return e.value;
      }
      return null;
   }
   
   final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
                return null;
        }
          //计算key的hash值
        int hash = (key == null) ? 0 : hash(key);
          //通过hash值确定存储table的下表
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
                e != null;
                e = e.next) {
            Object k;
              //1.先判断hash值是否相同,若相同则进行2
              //2.通过==判断key值是否相同,相同则返回值,不同进行3
              //3.通过.equals()方法判断是否相同
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                            return e;
        }
        return null;
}

remove方法

remove流程
1.通过传入的key进行删除,返回key所对应的value;
2.如果当前table没有存储值,则返回null;
3.对key进行哈希过程,得到hash。然后indexFor通过hash和table.length得到该key所对应的索引位置。
4.对该位置的链表进行遍历,记录下遍历节点的前节点prev和当前节点e;
5.如果第一个节点是要删除的,将key对应在数组中的位置第一个置为next;若不是 则将前驱prev的next置为next。

    public V remove(Object key) {  
        Entry<K,V> e = removeEntryForKey(key);  
        return (e == null ? null : e.value);  
    }  
    final Entry<K,V> removeEntryForKey(Object key) {  
        if (size == 0) {  
            return null;  
        }  
    //1.计算hash值
    int hash = (key == null) ? 0 : hash(key);  
    // 2. 计算存储下标位置
    int i = indexFor(hash, table.length);  
    Entry<K,V> prev = table[i];  
    Entry<K,V> e = prev;  
    while (e != null) {  
        Entry<K,V> next = e.next;  
        Object k;  
        if (e.hash == hash &&  
            ((k = e.key) == key || (key != null && key.equals(k)))) {  
            modCount++;  
            size--; 
            // 若删除的是table数组中的元素(链表的头结点) 
            // 将头结点的next引用存入table[i]中  
            if (prev == e) 
                table[i] = next;

            // 将以table[i]为头结点的链表中,当前Entry的前一个Entry中的next 设置为 当前Entry的next(直接跳过当前Entry)
            else  
                prev.next = next;   
            e.recordRemoval(this);  
            return e;  
        }  
        prev = e;  
        e = next;  
    }  

    return e;  
} 

containsKey方法

 直接返回getEntry(key) != null,如果key对应的Entry不为空,则containsKey为true;
public boolean containsKey(Object key) {  
    return getEntry(key) != null; 
} 

getEntry方法

   final Entry<K,V> getEntry(Object key) {  
        if (size == 0) {  
            return null;  
        }  
    
        // 1.通过hash(key)计算出对应的hash值
        int hash = (key == null) ? 0 : hash(key);  
    
        // 2. 根据hash值计算出对应的数组下标
        // 3. 遍历 以该数组下标的数组元素为头结点的链表所有节点,寻找该key对应的值
        for (Entry<K,V> e = table[indexFor(hash, table.length)];  e != null;  e = e.next) {  
    
            Object k;  
            // 若 hash值 & key 相等,则证明该Entry是需要的键值对
            // 通过equals()判断key是否相等
            if (e.hash == hash &&  
                ((k = e.key) == key || (key != null && key.equals(k))))  
                return e;  
        }  
        return null;  
    }  

containsValue

 如果value为null的话,对原table进行双重for循环遍历,找到value为null的就返回true;
 如果value不为null,对原table进行双重for循环,找到value.equals(e.value);返回true;
 public boolean containsValue(Object value) {  
        // 若value为空,则调用containsNullValue()  
        if (value == null)
            return containsNullValue();  
    
        // 若value不为空,则遍历链表中的每个Entry,通过equals()比较values 判断是否存在
        Entry[] tab = table;
        for (int i = 0; i < tab.length ; i++)  
            for (Entry e = tab[i] ; e != null ; e = e.next)  
                if (value.equals(e.value)) 
                    return true;//返回true  
        return false;  
    }  

containsNullKey方法

 private boolean containsNullValue() {  
        Entry[] tab = table;  
        for (int i = 0; i < tab.length ; i++)  
            for (Entry e = tab[i] ; e != null ; e = e.next)  
                if (e.value == null)
                    return true;  
        return false;  
    } 
CANoe的安装教程 阅读详情

相关推荐

mysql8.0安装及配置超详细教程_mysql-8.0.15-winx64

MySQL8.0配置教程, 全网最详,同时有本地mysql和小皮系统见另一篇

小诺言的博客 2万+

map集合存放的数据键值对

1、Map集合介绍 Map集合通过键值对(key-value)的形式来存储数据。(键值对:一个对象并不是单纯的只有一个内容,而是包含key和value两部分内容,key和value存在对应关系) Map中key可以为任意类型,实际开发建议使用String,value也可以是任意类型。 Map集合通过键获取值,查询速度非常快。 2、Map的实现类 HashMap(使用频率最高)(无序); TreeMap(自然升序); HashTable(无序); 三个实现类使用步骤类似,我们以HashMap为

m0_73875939的博客 994

Django 前后端分离实战:网络设备运维系统 - 从登录认证到业务模块

这篇文章分享了Django+Vue前后端分离开发网络设备运维系统的实战经验,重点介绍了登录认证、全局布局和设备管理三大模块的实现。文章包含以下核心内容: 登录模块实现:包括表单验证、Token认证、记住密码功能,以及美观的UI设计(渐变背景、毛玻璃效果) 全局布局架构:采用侧边栏+顶部导航的响应式设计,支持菜单权限控制和多设备适配 业务功能开发:详细展示了设备分类和部门管理的CRUD功能实现,包括列表展示、搜索过滤、分页等功能 常见问题解决:提供了5个开发中常见问题的解决方案,如Vue Router警告、C

qq_16766705的博客 526

Java基础之Map集合

1.Map概述        Map 实现Map接口的集合类来存储元素对及“键-值”对,其中不能包含重复的键,每个键最多只能一个值,值可以重复。 2.JDK API中Map接口实现的子类有 (1)HashMap (2)TreeMap (3)HashTable 3.Map接口中的常用方法          Object put(Object key,Object value);//将指

west_xia_616的博客 222

matlab内读取文件目录时斜杠和反斜杠的使用

@matlab内读取文件目录时斜杠和反斜杠的使用TOC matlab内读取文件目录时,斜杠和反斜杠的使用 最近被斜杠和反斜杠弄晕了,于是总结了一下,在此和大家分享一下。 代码 clear; clc; close all; I1= imread('..\zs1.jpg'); %和代码文件夹同目录的图片 (纯反斜杠) I2= imread('../zs2/zs2.j...

weixin_44456694的博客 6575

VLM,VLA,VLN的区别与联系

VLM 提供了理解世界的“眼睛和大脑”,VLN 赋予了这个智能体在空间中“移动的双腿”,而 VLA 则进一步赋予了它“灵巧的双手”来改变世界。三者共同构成了一个完整具身智能体的核心能力模块。

l0409044222的博客 1314

Java基础——HashMap/ArrayList/LinkedList/反射

2、hashMap put get过程 3、hashmap什么时候退回链表?为什么不是7 4、jdk1.8引入红黑树后,如果单链表节点个数超过8个,是否一定会树化? 5、HashMap扩容的原因? 6、hashmap扩容时每个entry需要再计算一次hash吗? 4、ArrayList与LinkedList的区别 https://blog.youkuaiyun.com/weixin_44844089/article/details/105856813 5、JAVA类加载过程 6、哈希表的作用 哈希表使用的

weixin_38526781的博客 334

Python入门基础教程(非常详细)

Python是一门开源免费、通用型的脚本编程语言,它上手简单,功能强大,坚持「极简主义」。Python类库(模块)极其丰富,这使得Python几乎无所不能,不管是传统的Web开发、PC软件开发、Linux运维,还是当下火热的机器学习、大数据分析、网络爬虫,Python都能胜任。这套Python基础教程不是教科书,不会玩弄概念,而是力求口语化和通俗化,让读者尽快入门。如果有小朋友出于兴趣学习Python,请引导他阅读本教程的前半部分,这是基础,可以降低学习成本;...

Yuki1127918的博客 34万+

Map储存键值对

/一支树上的元素低于6个这个树就会退化成链。//如果数组的树化阈值没有达到64则优先树化。TreeMap的底层实现是TreeSet。HashMap的底层实现是HashSet。//容量阈值达到容量的0.75就开始扩容。key不能为null, key是唯一的。key和value都不可以为null。key可以为空的但只能有一个为空。//一个链达到8就对该链进行树化。value值可以为null。//数组的默认容量是16,//每次扩容两倍向左移一位。"删除的value值:"//最小树化容量阈值64。

Er222220的博客 381

华为防火墙双线路配置实战:5分钟搞定主备自动切换(含健康检查避坑指南)

本文详细介绍了华为防火墙双线路配置实战,包括主备自动切换和健康检查机制的优化技巧。通过智能健康检查机制,华为防火墙能实现毫秒级故障自动切换,确保网络高可用性。文章提供了从架构设计到实战配置的全流程指南,并分享了常见配置误区与解决方案,帮助中小企业快速搭建稳定可靠的网络架构。

weixin_30872337的博客 606

个人信息保护合规审计人员(中级)能力测试试卷

本文是关于个人信息保护合规考试的试题解析,内容涵盖《个人信息保护法》相关规定。试卷包含单选题、多选题、判断题、简答题和案例分析题五种题型,总分100分,考试时间120分钟。试题重点考察个人信息处理的基本原则、敏感个人信息处理要求、合规审计流程、个人信息保护影响评估等内容。通过案例分析题详细解析了人脸识别登录、数据共享、跨境传输等场景下的合规要求。参考答案均严格依据《个人信息保护法》等法律法规,为个人信息处理活动提供了明确的合规指引。

qq_41525079的博客 857

HashMap

一、HashMap结构及原理 HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。 新建一个HashMap的时候,就会初始化一个数组,默认大小为16。Node就是数组中的元素,每个Node其实就是一个key-value的键值对,它持有一个指向下一个元素的引用,这就构成了链表,HashMap底层将key-...

K_Raytheon的博客 664

RK3588-NANOPC-T6移植ubuntu+xenomai内核

更具网址下载相应的内核。导入到开发板并执行内核。

qq_42007446的博客 574

【Java】HashMap的详细介绍

扩容过程中,节点会从旧数组迁移到新数组,头插法在迁移时会反转链表顺序(例如旧链表 A→B,迁移后新链表变为 B→A)。若此时有多个线程同时操作,可能出现节点引用相互指向的情况(如 A.next = B 且 B.next = A),形成环形链表。后续查询时,线程会陷入无限循环,导致 CPU 占用飙升。原因是:数组容量小时,扩容成本低,通过扩容可分散元素,减少冲突;基于泊松分布,当负载因子为 0.75 时,链表长度自然增长到 8 的概率极低(约千万分之一),此时说明哈希冲突异常频繁,需要转为红黑树优化。

chaodddddd的博客 1779

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

有关HashMap的介绍

HashMap

qq_49194786的博客 799

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

HashMap 的简单介绍

HashMap的几个知识点 1. HashMap 是以key–value对的形式存储的,key值是唯一的,一个key只能对应着一个value,但是value是可以重复的 2. HashMap 如果再次添加相同的key值,它会覆盖key值所对应的内容,这也是与HashSet不同的一点,Set通过add添加相同的对象,不会再添加到Set中去 3. HashMap 提供了get方法,通过key值取对

请叫我王老魔 745

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Java HashMap详细介绍

目录第1部分 HashMap介绍第2部分 HashMap数据结构第3部分 HashMap源码解析(基于JDK1.6.0_45)第3.1部分 HashMap的“拉链法”相关内容3.1.1 HashMap数据存储数组3.1.2 数据节点Entry的数据结构第3.2部分 HashMap的构造函数第3.3部分 HashMap的主要对外接口3.3.1 clear()3.3.2 containsKey()3....

忘尘的博客 454

HashMap介绍以及常用方法

HashMap 基于哈希表的 Map 接口实现,是以 key-value 存储形式存在,即主要用来存放键值对。HashMap 的实现不是同步的,这意味着它不是线程安全的。它的 key、value 都可以为 null,此外,HashMap 中的映射不是有序的。jdk1.8 之前 HashMap 由 数组 + 链表 组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突(两个对象调用的 hashCode 方法计算的哈希值经哈希函数算出来的地址被别的元素占用)而存在的(“拉链法”解决冲突)。

m0_54049074的博客 518

HashMap详细介绍

HashMap详细介绍 目录 HashMap详细介绍 第1部分 HashMap介绍 第2部分 HashMap数据结构 第3部分 HashMap源码解析(基于JDK1.6.0_45) 第4部分 HashMap遍历方式 第5部分 HashMap示例 转载自: http://www.cnblogs.com/skywang12345/p/3310835.html 我们先对HashMap有...

u013377588的博客 422

HashMap的put过程以及hashMap的简单介绍

本文介绍了HashMap的基本原理和实现机制。HashMap采用数组+链表+红黑树(JDK1.8后)的数据结构,键值允许为null但键唯一。主要特点包括:初始容量16(JDK8后首次put时创建),哈希冲突时通过equals比较处理,当链表长度超过8且数组长度大于64时转换为红黑树以提高查询效率。扩容机制为双倍增长,临界值为容量乘以加载因子(默认0.75)。put操作包含空数组检测、哈希计算、冲突处理(覆盖或新增节点)、树化判断等步骤,完整展现了HashMap的核心运作流程。

2302_81352523的博客 460

Java-HashMap介绍

HashMap 是 Java 中最常用的键值对存储结构之一,它基于哈希表实现,具有高效的查找、插入和删除操作

czl5253的博客 1192

HashMap 简单介绍

t(1, "abc"); map.put(2, "ABC"); map.put("a", "msf"); //如果出现了相同的键的时候 , 那么值将会进行覆盖 //迭代器 获取键值 Iterator s = map.keySet().iterator(); //取值 s.hasNext() ; 判断是否有值 next(); 方法获取当前值 while(s.hasNex

SUNZONE的专栏 130

Java8HashMap源码介绍

简介以及内部实现 Java为数据结构中的映射定义了一个接口java.util.Map,此接口主要有四个常用的实现类,分别是HashMap、Hashtable、LinkedHashMap和TreeMap,这里主要介绍HashMap。想要弄明白HashMap,我们就先要了解HashMap是什么,即它的数据存储结构,其次要弄清楚HashMap能干什么,即它的功能实现。 存储结构 从结构实现来讲,H...

wand1995的博客 160
上一篇: HashMap和HashTable的异同点
下一篇: 简单麻将功能实现
Java-fly
博客等级 码龄9年 13粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值