`
benx
  • 浏览: 276307 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

HashMap 源码分析

    博客分类:
  • java
阅读更多

 

今天面试提到了HasmMap,之前也有看过其源代码,了解其原理,不过又忘得差不多,今天就在读下,加深印象。

 

1、HashMap得内部元素以Entry存在,继承Map.Entry,元素包含,Entry相当于一个LinkedList

 

       final Object key;
        Object value;
        final int hash;
        Entry next;

        Entry(int i, Object obj, Object obj1, Entry entry)
        {
            value = obj1;
            next = entry;
            key = obj;
            hash = i;
        }

 

 

2、HashMap得变量有

 

    
    static final int DEFAULT_INITIAL_CAPACITY = 16;   //默认容量
    static final int MAXIMUM_CAPACITY = 1073741824;  //最大容量
    static final float DEFAULT_LOAD_FACTOR = 0.75F;   //默认加载因子
    transient Entry table[];    //HashMap得Entry集合
    transient int size;     //当前数量
    int threshold;           //容量*加载因子,当size>threshold时就会resize
    final float loadFactor;    //加载因子
    volatile transient int modCount;    //修改计量,在迭代器中使用,如果迭代中有修改modCount那么会抛出ConcurrentModificationException()异常  
    static final Object NULL_KEY = new Object();    //默认空值
    private static final boolean useNewHash = false;
    private transient Set entrySet;

 

 

 

3、put方法

 

 public Object put(Object obj, Object obj1)
    {
        if(obj == null)
            return putForNullKey(obj1);    //新增一个 newObjedct(),如果有null key,返回一个存在的值
        //计算HashCode
        int i = hash(obj.hashCode());
        //计算位置   相当于hashCode mod table.length  使新增的对象能平均分配到各个桶中
        int j = indexFor(i, table.length);
        //首先根据index找到桶,然后循环桶中的Entry
        for(Entry entry = table[j]; entry != null; entry = entry.next)
        {
            Object obj2;
            //根据hashCode和(equal or ==)比较新增key是否存在,如果存在就覆盖以前的值,并返回
            if(entry.hash == i && ((obj2 = entry.key) == obj || obj.equals(obj2)))
            {
                Object obj3 = entry.value;
                entry.value = obj1;
                entry.recordAccess(this);
                return obj3;
            }
        }

        modCount++;
        //新增entry
        addEntry(i, obj, obj1, j);
        return null;
    }

 

 

 

4、addEntry

 

/**
     * 新增一个Entry
     * @param i    hashCode
     * @param obj  key
     * @param obj1   value
     * @param j   index,桶的位置
     */
    void addEntry(int i, Object obj, Object obj1, int j)
    {
    	//找到桶的Entry
        Entry entry = table[j];
        //在桶中新增一个Entry,相当于Entry.next = new Entry()
        table[j] = new Entry(i, obj, obj1, entry);
        //如果HashMap中的size大于容量*加载因子,就需要扩容,把容量*2,
        //从这里可以看出,HashMap希望一个Entry就一个值,尽量少的next,如果HashCode分布不均匀,那么可能数据都分配到少数桶中,使HashMap等于一个LinkedList
        if(size++ >= threshold)
            resize(2 * table.length);
    }
 

 

5、resize扩容

 

 

 //HashMap扩容
    void resize(int i)
    {
        Entry aentry[] = table;
        int j = aentry.length;
        if(j == 1073741824)
        {
            threshold = 2147483647;
            return;
        } else
        {
        	//重新分配内存
            Entry aentry1[] = new Entry[i];
            transfer(aentry1);
            table = aentry1;
            threshold = (int)((float)i * loadFactor);
            return;
        }
    }

    void transfer(Entry aentry[])
    {
        Entry aentry1[] = table;
        int i = aentry.length;
        //循环桶
        for(int j = 0; j < aentry1.length; j++)
        {
            Entry entry = aentry1[j];
            if(entry == null)
                continue;
            aentry1[j] = null;
            //遍历Entry,重新Hash,从这可以看出,如果可以预见HashMap的数量,那么就最好指定桶的数量,不然随着数量的增多就需要resize()
            //resize是非常耗性能的步骤,需要把所有的值重新resize,然后分配到各个桶中
            do
            {
                Entry entry1 = entry.next;
                int k = indexFor(entry.hash, i);
                entry.next = aentry[k];
                aentry[k] = entry;
                entry = entry1;
            } while(entry != null);
        }

    }

 

 

6、get方法

 

 /**
     * 这个方法很简单,就是根据hashCode获取桶,然后遍历桶找key
     */
    public Object get(Object obj)
    {
        if(obj == null)
            return getForNullKey();
        int i = hash(obj.hashCode());
        for(Entry entry = table[indexFor(i, table.length)]; entry != null; entry = entry.next)
        {
            Object obj1;
            if(entry.hash == i && ((obj1 = entry.key) == obj || obj.equals(obj1)))
                return entry.value;
        }

        return null;
    }
 

 

 

6、迭代器

 

/**
     * keyIterator,valueIterator的父类,这里都是内部类,好处是可以随意的使用外部HashMap的变量和属性
     * @author Administrator
     *
     */
    private abstract class HashIterator
        implements Iterator
    {

    	/**
    	 * 构造函数,特别注意expectedModCount = modCount;这个玩意说明HashMap的Iterator是不安全的,如果在遍历过程中有修改HashMap那么就Exception了
    	 */
    	 HashIterator()
         {
             this$0 = HashMap.this;
             super();
             expectedModCount = modCount;
             HashMap.Entry aentry[] = table;
             int i = aentry.length;
             HashMap.Entry entry = null;
             if(size != 0)
                 while(i > 0 && (entry = aentry[--i]) == null) ;
             next = entry;
             index = i;
         }
    	 
        public boolean hasNext()
        {
            return next != null;
        }

        /**
         * 
         * @return
         */
        HashMap.Entry nextEntry()
        {
            if(modCount != expectedModCount)
                throw new ConcurrentModificationException();
            HashMap.Entry entry = next;
            if(entry == null)
                throw new NoSuchElementException();
            HashMap.Entry entry1 = entry.next;
            HashMap.Entry aentry[] = table;
            int i;
            for(i = index; entry1 == null && i > 0; entry1 = aentry[--i]);
            index = i;
            next = entry1;
            return current = entry;
        }

        /**
         * 由这个方法说明可以通过Iterator可以删除HashMap
         */
        public void remove()
        {
            if(current == null)
                throw new IllegalStateException();
            if(modCount != expectedModCount)
            {
                throw new ConcurrentModificationException();
            } else
            {
                Object obj = current.key;
                current = null;
                removeEntryForKey(obj);
                expectedModCount = modCount;
                return;
            }
        }

        HashMap.Entry next;
        int expectedModCount;
        int index;
        HashMap.Entry current;
        final HashMap this$0;

       
    }
 

 

总结:

1、HashMap是根据Key的HashCode来存储和获取value的,所以Key对象最好是重载HashCode()和equals()方法,否则会导致put和get出现问题,同时也使性能出现问题

 

2、如果知道存储HashMap的数量,最好是指定其桶的数量,默认是16(有点小),否则会频繁的resize,每次把当前桶的数量乘以2,然后重新Hash和分配存储位置,这是个非常耗性能的过程。

 

3、加载因子,默认是0.75,当HashMap.size > 桶数量*加载因子时就会resize(),这个最好不要修改,保留默认值。如果过高会导致性能降低,如果过低,会浪费空间,同时会导致频繁resize()

 

4、HashMap是允许null的,相当于存储一个new Object();貌似意义不大

 

5、HashMap的迭代器是不安全的,如果在迭代器外部修改HashMap,比如add(),remove(),都会导致迭代过程中报错。

 

 

 

 

 

 

 

 

 

 

 

 

0
2
分享到:
评论

相关推荐

    HashMap源码分析

    HashMap 是 Java 中最常用的集合类之一,它是基于哈希表实现的,提供了高效的数据存取功能。HashMap 的核心原理是将键(key)通过哈希函数转化为数组索引,然后将键值对(key-value pair)存储在数组的对应位置。...

    1.面试必考之HashMap源码分析与实现 伸缩性角度看HashMap的不足

    1.面试必考之HashMap源码分析与实现 伸缩性角度看HashMap的不足

    面试必考之HashMap源码分析与实现

    面试中,HashMap的源码分析与实现是一个常见的考察点,因为它涉及到数据结构、并发处理和性能优化等多个核心领域。本篇文章将深入探讨HashMap的内部工作原理、关键特性以及其在实际应用中的考量因素。 HashMap基于...

    HashMap源码分析-思维导图.xmind

    hashmap的原理啊思想。

    HashMap源码分析系列-第四弹:HashMap多线程解决方案.docx

    #### 二、HashMap线程安全问题分析 在多线程环境中,`HashMap`的主要线程安全问题包括但不限于: 1. **链表死循环问题**:在JDK 1.7中,当多个线程同时进行`put`操作时,可能会出现链表死循环的情况,这是一个严重...

    Java集合系列之HashMap源码分析

    Java集合系列之HashMap源码分析 Java集合系列之HashMap源码分析是Java集合系列中的一篇非常重要的文章,它详细介绍了Java集合系列之HashMap源码,具有很高的参考价值。下面我们将对HashMap源码进行详细的分析。 ...

    hashmap实现原理

    哈希映射(HashMap)是Java编程语言中广泛使用的数据结构之一,主要提供键值对的存储和查找功能。HashMap的实现基于哈希表的概念,它通过计算对象的哈希码来快速定位数据,从而实现了O(1)的平均时间复杂度。在深入...

    Java高级面试第二套1.面试必考之HashMap源码分析与实现

    微信小程序详细图文教程 泉州大白网络科技 目录 一.微信小程序申请 二....1.申请服务器 2.部署服务器 3.域名申请和配置 三....一....申请,并认证(未认证不能发布,认证需要300元,目前只支持企业认证)详细见官网说明。...

    JAVA之hashmap源码分析-Mobile-Dev-Analysis:android或java的分析

    JAVA之hashmap源码分析 Mobile-Dev-Analysis Analysis of android or java 红岩网校工作站移动开发部学员分组学习 为了让大家学的更加坚固,采取小组学习的方式帮助大家学习,同时在学习研究的过程中需要不断的做...

    JAVA之hashmap源码分析-haha:已弃用的Java库可自动分析Android堆转储

    JAVA之hashmap源码分析 无头Android堆分析器 “哈哈!” -纳尔逊 此存储库已弃用 创建该项目的目的是通过重新打包其他存储库中的堆转储解析器来提供堆转储解析器。 LeakCanary现在有它自己的。 该解析器在Maven ...

    JAVA之hashmap源码分析-superword:Superword是一个Java开源项目,致力于英语单词分析和辅助阅读的研究

    JAVA之hashmap源码分析Superword是Java开源项目,致力于研究英语单词分析和辅助阅读,包括但不限于拼写相似度,定义相似度,发音相似度,拼写转换规则,前缀和动态前缀,后缀以及动态后缀,词根,复合词,文本辅助...

    【死磕Java集合】-集合源码分析.pdf

    四、HashMap源码分析 HashMap是一种基于散列表实现的Map,提供了快速的键值对存储和检索能力。HashMap的继承体系中,它继承了AbstractMap,实现了Map接口。 HashMap的主要属性包括键值对数组table、键值对个数size...

    Java面试专属视频

    面试必考之HashMap源码分析与实现 ,微服务架构之Spring Cloud Eureka 场景分析与实战,高性能必学之Mysql主从架构实践 ,架构师不得不知道的Spring事物不能回滚的深层次原因 ,分库分表之后分布式下如何保证ID全局...

    Android(Java) 模拟登录知乎并抓取用户信息

    在本文中,我们将深入探讨如何使用Java在Android平台上实现模拟登录知乎并抓取用户信息的过程。... 首先,我们需要了解的是Android的网络访问机制。Android系统为了安全性和用户体验,对网络访问进行了限制,必须在...

    HashMap源码剖析共10页.pdf.zip

    《HashMap源码剖析》 HashMap是Java编程语言中一个非常重要的数据结构,它在实际开发中被广泛应用。作为集合框架的一部分,HashMap实现了Map接口,提供了高效、非同步的键值对存储功能。在这个10页的PDF文档中,...

    手写HashMap源码.rar

    本篇文章将通过分析一个名为"MyHashMap"的手写HashMap源码,来探讨HashMap的内部机制,帮助提升编程能力。 首先,HashMap的核心是基于哈希表的数据结构,它利用键(Key)的哈希函数将键映射到数组的特定位置,从而...

Global site tag (gtag.js) - Google Analytics