`

hashmap 源码阅读

阅读更多

hashmap 在开发中用的很多,看下源码实现学习一下,

 

字段

    static final int DEFAULT_INITIAL_CAPACITY = 16;//默认的数组长度


    static final int MAXIMUM_CAPACITY = 1 << 30;//最大的数组长度


    static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认负载比率,数组不会全部填满数据 

  
    transient Entry[] table;//hashmap内部也是用数组来存储数据


    transient int size;//实际存的数据的个数

 
    int threshold;//实际当前可以存的数据量 = loadFactor*table.length
 

    final float loadFactor; //实际的负载比率

 

构造方法

    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;    //不管你希望的初始长度是多少,系统都是对1做移位运算,最后的结果只能是2的次方

        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);  //计算实际容量
        table = new Entry[capacity];  // 初始数组
        init();    //留给子类实现的一些操作,默认空
    }

 

put方法

 

    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

 // 首先根据内部的一个hash算法计算出key对应的hash值(防止key本身的hashcode实现很差),再根据这个hash值和表的长度算出一个数组的index值,  然后在数组的该index值上看有没有存东西,东西一不一致(equals),不一致查找entry链表下一个(关于链表下面有说明),直到找到一个null, 就可以确定没有了,然后准备插入

 

    void addEntry(int hash, K key, V value, int bucketIndex) {
	Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }

 entry

    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

 entry实际上是在实现单向链表结构,每一个entry内部有一个next指向他的后面,所以hashmap的内部数组每个格子存放的是实际上是一个链表的火车头,在最开始的时候table[index]位置上肯定是null,这个就是火车尾了。所以get的时候查找操作是以null为循环终点判断依据的。

在插入的时候把新加的元素替代原来的成为链表火车头,把之前元素的放在新加的火车头后面,

这么做是因为不同的key是有可能算出相同的hash值的,这种情况的时候,他们都会存放在数组的同一个index位置上,相同的hash不同的key的值彼此用链表连接起来,

 

get

    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }

 get的时候就是算出key的hash值,算出数组的index位置,然后到数组的index位置上去遍历链表找到相应的key,如果hash重复的少的话,链表的长度就会很短,hashmap查找的速度就很快了,即使有重复,接下来就是遍历链表,所以要依靠好的算法保证hash重复的概率小,缩短链表的长度

 

假如2个对象的key最后算出来的index值一样呢, 就是这2个对象存在一条链表上, 那么get的时候会不会拿错了呢

 

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

 

看这行代码就知道了, 链表上的实体存了自己的key的, 所以即使不同的实体是放在一个index上,也可以通过不同的key来区分的

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    HashMap源码深度剖析.md

    HashMap源码深度剖析,面试必备

    HashMap源码(JDK1.7,含注释)

    HashMap源码(JDK1.7,含注释)

    HashMap源码(上)

    HashMap的部分源码解析

    一个delphi的hashmap源代码

    在IT行业中,哈希表(HashMap)是一种高效的数据结构,它使用哈希函数将键(Key)映射到数组的特定位置,以便快速存取数据。...通过阅读和分析源代码,我们可以学习如何在实际项目中应用哈希表,提高数据操作的效率。

    1.6 hashmap源码

    hashmap源码,可以看看http://blog.csdn.net/wabiaozia/article/details/50684556

    HashMap之resize()方法源码解读.docx

    HashMap之resize()方法源码解读 HashMap的resize()方法是HashMap中最核心的方法之一,该方法负责扩容HashMap的容量,以便存储更多的键值对。下面我们将对HashMap的resize()方法进行源码解读,了解其扩容机制和原理...

    HashMap源码流程图

    HashMap源码流程图 一图解析HashMap源码流程 // 默认的HashMap中数组的长度 16 static final int DEFAULT_INITIAL_CAPACITY = 1 ; // aka 16 // HashMap中的数组的最大容量 static final int MAXIMUM_CAPACITY = 1 ...

    JDK7HashMap源码

    精确的版本号是jdk-7u80。想不通,竟然很多人都收费,这个明明可以在安装JDK的目录中找到啊!自己下一个JDK就可以得到。

    hashMap1.8源码

    HashMap是Java编程语言中最常用的集合类之一,它提供了一种基于键值对(key-value pair)的数据存储方式,具有高效查找、插入和删除操作。...对于学习者来说,阅读源码并结合实践是掌握HashMap的最好方式。

    HashMap源码分析

    HashMap 是 Java 中最常用的集合类之一,它是基于哈希表实现的,提供了高效的数据存取功能。HashMap 的核心原理是将键(key)通过哈希函数转化为数组...理解HashMap的内部机制对于优化代码性能和避免潜在问题非常重要。

    JDK8HashMap源码

    精确的版本号是jdk-8u181。想不通,竟然很多人都收费,这个明明可以在安装JDK的目录中找到啊!自己下一个JDK就可以得到。

    java代码-使用java解决手写hashMap的源代码

    java代码-使用java解决手写hashMap的源代码 ——学习参考资料:仅用于个人学习使用!

    HashMap jdk1.8源码阅读.md

    本人源码阅读理解, 基于jdk1.7 和jdk 1.8的java HashMap源码的理解, 希望对你理解java源码有作用

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

    在Java编程语言中,HashMap是集合框架中一个重要的类,用于存储键值对的数据结构。面试中,HashMap的源码分析与实现是一个常见的考察点,...深入学习和实践HashMap源码,能够帮助我们更好地理解和优化Java应用程序。

    hashmap实现原理

    哈希映射(HashMap)是Java编程语言中广泛使用的...了解这些实现细节对于优化代码性能和避免潜在问题至关重要。在实际编程中,应充分考虑哈希冲突的处理、负载因子的选择以及预估容量的设定,以提高HashMap的使用效率。

    易语言源码易语言HashMap类源码.rar

    在给定的压缩包“易语言源码易语言HashMap类源码.rar”中,包含了易语言实现的HashMap类的源代码。HashMap是一种常见的数据结构,在许多编程语言中都有实现,它提供了快速的键值对存储和查找功能。 HashMap类是基于...

    HashMap源码阅读

    本文将深入探讨HashMap的源码,解析其内部实现机制,包括底层数据结构、哈希冲突解决、1.7和1.8版本的区别、扩容机制以及红黑树的左右旋操作。 首先,HashMap的底层数据结构是一个动态调整大小的位桶数组(bucket ...

Global site tag (gtag.js) - Google Analytics