22.源码阅读(jdk1.6 HashMap源码和原理分析)

HashMap 底层采用数组 + 链表的的实现方式来降低数据插入和查询的时间复杂度,理想状态下可以实现时间复杂度位O(1),今天就从源码的角度看一下它是如何实现的。我们从它的两个关键方法put和get入手。

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);
        //数组中i位置存在Entry对象
        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;
            }
        }
        //不存在则创建一个新的Entry加入数组,并将计数器加1
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

这里有两个关键方法hash和indexFor,hash()方法的作用是对key的hashCode值进行一系列的位运算,看下边的源码

/**
     * Applies a supplemental hash function to a given hashCode, which
     * defends against poor quality hash functions.  This is critical
     * because HashMap uses power-of-two length hash tables, that
     * otherwise encounter collisions for hashCodes that do not differ
     * in lower bits. Note: Null keys always map to hash 0, thus index 0.
     * 注释的意思大概是说下边代码是对给定的哈希值应用补充散列函数
     * 我们只需要知道一点,经过这些位运算之后,hash值的散列效果会
     * 得到优化即可
     */
    static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

再看indexFor方法,将得到的hash值与hashMap中数组的长度-1的值进行与运算,得到的就是这个元素将会被放置在数组中的哪个位置的index,并且绝不用担心发生数组越界之类的问题,原因请自行发现

   /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }

我们再回到上边的put方法中,获取到当前元素将要插入的index后,会从当前的这个内置的数组中查找这个index位置是否已经存放了Entry,如果有则变量这个index位置的链表,判断是否有相同key值的元素存在(当前的这个数组指的就是在new HashMap的时候默认创建出来的一个Entry数组,它有一个默认长度,此时在没有进行put操作的时候,数组中元素都是null)如果此时是第一次put,那么数组中是空的,直接绕过这个for循环往下执行。如果进行put操作的时候,数组中存这个index,那么会进入这个for循环,拿到i这个位置的单链表,遍历链表中所有的Entry,如果有相同key的元素,则找到它,用新的value值替换旧value,并返回旧value

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;
            }
        }

继续往下,如果不存在,那么创建新的Entry对象
addEntry(hash, key, value, i)
hash:key值计算得到的hash值
i: 经过hash和indexFor方法计算得到的被put的key,value即将被放入数组中的位置

   /**
     * Adds a new entry with the specified key, value and hash code to
     * the specified bucket.  It is the responsibility of this
     * method to resize the table if appropriate.
     *
     * Subclass overrides this to alter the behavior of put method.
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        //拿到数组中bucketIndex位置的Entry对象,第一次执行,他是null
    Entry<K,V> e = table[bucketIndex];
        //new了一个Entry对象放在这个bucketIndex位置上
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        //判断是否已经达到了需要扩容的条件,如果是则对HashMap进行扩容
        if (size++ >= threshold)
            resize(2 * table.length);
    }

Entry构造方法中第四个参数很重要,我们知道HashMap是基于数组和单链表的,数组就是指的Entry数组,而单链表呢?就在Entry对象的这个next属性上

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

添加一个Entry到数组中某一位置后,会同时给它指定它的next,这个next就是它所指向的下一个Entry元素,这样,每次添加一个Entry都会把这个Entry加入到这个链表的第一个位置,它的next就是原来在第一个位置的Entry对象,这样就连成了一个链子

添加了第一个Entry到数组中某一index后,它的next指向的是一个null,因为在没有添加的时候,这个index的位置存放的就是null值

此时我们的数组中添加了第一个Entry,此时会判断当前数组的长度是否已经达到了极值(每次添加都会进行一次判断),如果是则对数组进行扩容,执行 resize(2 * table.length)可以看到,每次会扩容一倍的长度

    /**
     * Rehashes the contents of this map into a new array with a
     * larger capacity.  This method is called automatically when the
     * number of keys in this map reaches its threshold.
     *
     * If current capacity is MAXIMUM_CAPACITY, this method does not
     * resize the map, but sets threshold to Integer.MAX_VALUE.
     * This has the effect of preventing future calls.
     *
     * @param newCapacity the new capacity, MUST be a power of two;
     *        must be greater than current capacity unless current
     *        capacity is MAXIMUM_CAPACITY (in which case value
     *        is irrelevant).
     */
    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        //判断当前数组的长度是否已经达到了设置的MAXIMUM_CAPACITY
        //如果是则将threshold (达到这个值就满足扩容的条件),设置为int的最大值
        //那么此时hashMap停止扩容,可见,hashMap容量是有极限的
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
        //创建一个新的Entry数组
        Entry[] newTable = new Entry[newCapacity];
        //将旧的数组中的值经过转换放入新的数组中
        transfer(newTable);
        //使用扩容后的数组
        table = newTable;
        //扩容阈值更新
        threshold = (int)(newCapacity * loadFactor);
    }

HashMap扩容的原理也很简单,因为它是基于数组实现的,所以所谓的扩容并不是将原有的数组长度扩大,而是创建了一个新的数组,这个数组的长度等于扩容后的长度,然后将旧数组中的元素转移到新数组中,旧的数组废弃,这个转移的过程仍然是经过了一系列的变换的,我们知道之前的数组中每一个put进来的元素的index都是经过一系列的位运算得到的,那么这里在进行转移的时候同样要再次进行运算得到新的index,因为这个index是基于hash值和数组长度生成的,hash值不会改变但是数组长度经过扩容后是不同了,所以要重新计算,否则会导致查询数据失败

   /**
     * Transfers all entries from current table to newTable.
     */
    void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        //遍历旧的数组中所有的Entry对象
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            //拿到index位置的Entry后,如果这个Entry不为null,则循环遍历这个链表
            if (e != null) {
                src[j] = null;
                do {
                    //拿到链表上每一个Entry
                    Entry<K,V> next = e.next;
                    //计算这个Entry在扩容后数组中的index值
                    int i = indexFor(e.hash, newCapacity);
                    //设置这个Entry的next值
                    e.next = newTable[i];
                    //把这个Entry放在计算得到的index位置上
                    newTable[i] = e;
                    //赋值获取下一个Entry,直到这个链表遍历完成
                    e = next;
                } while (e != null);
            }
        }
    }

源码是这样的先后顺序放置的,如果你把顺序调换一下会更好理解。这里我们先调整一下代码的顺序如下(不影响结果,但是更易理解)。大概过程就是假设我第一个找到了index=1的位置的这个单链表(这个链表可能只有一个元素,也可能有多个),我会获取到这个位置的第一个Entry,计算得到它在新数组中的index,然后把这个Entry放在这个数组的index位置上,将它的next指定为原本这个位置的值,其实就是null,这是第一步,第二步就是获取到第一个Entry的next值,也就是链表中第二个位置的Entry,把这个Entry重新指定为e,重复上边的操作,这样一整个过程下来,旧的数组中的所有元素都被正确的放置在了新数组中的正确位置上,达到了扩容的效果

  do {
        //计算这个Entry在扩容后数组中的index值
        int i = indexFor(e.hash, newCapacity);
        //把这个Entry放在计算得到的index位置上
        newTable[i] = e;
       //设置这个Entry的next值
        e.next = newTable[i];

        //拿到链表上每一个Entry
        Entry<K,V> next = e.next;
        //赋值获取下一个Entry,直到这个链表遍历完成
        e = next;
  } while (e != null);
get方法

put看完之后get就简单多了。根据传入的key值,进行hash和indexFor运算,得到key在数组中的index,然后拿到数组中index位置的entry对象,再从这个位置的单链表中查找key值相同的value返回

    /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     *
     * @see #put(Object, Object)
     */
    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;
    }

总结一下:
1.HashMap底层基于数组和单链表
2.hash()和indexFor()方法作用很关键
3.如果你去看看native曾hashCode值获取的方式,你会得到两个结论(hashCode值并不是简单的地址值,而是地址值进行右移16位之后的一个值,参阅ndk底层c++代码实现):

两个不同的对象 hashCode 值可能会相等,hashCode 值不相等的两个对象肯定不是同一对象。

附上手写的HashMap代码


public class MyHashMap<K,V> {
    
    public MapEntry<K,V>[] table;
    
    int size;
    
    /**
     * 扩容阈值,达到这个值时就要扩容
     */
    int threshold = 8;
    
    /**
     * 扩容因子
     */
    final float loadFactor = 0.75f; 
    
    public class MapEntry<K,V>{
        private K key;
        private V value;
        MapEntry<K,V> next;
        int hash;
        
        public MapEntry(int hash,K key,V value,MapEntry<K,V> next) {
            this.key = key;
            this.value = value;
            this.hash =hash;
            this.next = next;
        }
    }

    public V put(K key,V value) {
        if(table == null) {
            table = new MapEntry[8];
        }
        
        //判断key为null
        if(key == null) {
            return null;
        }
        
        int hash = hash(key);
        int index = getIndex(hash,table.length);
        System.out.println("key = "+key+" hash="+hash+" index="+ index);
        //判断这个key是否存在
        for (MapEntry<K,V> e = table[index]; e!=null;e = e.next) {
            Object k = null;
            if(e.hash == hash && ((k = e.key) == key) || (key.equals(k))){
                V oldV = e.value;
                e.value = value;
                return oldV;
            }
        }
        
        //添加新的
        addEntry(hash,key,value,index);
        return null;
    }
    
    private void addEntry(int hash, K key, V value, int index) {
        //hash值相等,两个对象不一定相等,两个对象不相等,hash值有可能相等
        //hashCode值 从native看,mask_bits(value()>> hash_shift,hash_mask))
        //是地址右移16位的结果
        if(size >= threshold && table[index] != null) {
            //右移一位,扩容一倍
            resize(size << 1);
            //重新计算index
            index = getIndex(hash,table.length);
        }
        //添加
        createEntry(hash,key,value,index);
        size++;
    }

    private void createEntry(int hash, K key, V value, int index) {
        MapEntry<K, V> newEntry = new MapEntry<K, V>(hash, key, value, table[index]);
        table[index] = newEntry;
    }

    private void resize(int newCapacity) {
        MapEntry<K, V> [] newTable = new MapEntry[newCapacity];
        //将原来数组中的内容经过转换之后放入新数组
        transform(newTable);
        table = newTable;
        threshold = (int) (newCapacity*loadFactor);
        System.out.println("扩容之后:newCapacity="+newCapacity+" threshold="+threshold);
    }

    /**
     * 重新计算散列
     * @param newTable
     */
    private void transform(MyHashMap<K, V>.MapEntry<K, V>[] newTable) {
        int newCapacity = newTable.length;
        for(MapEntry<K,V> e:table) {
            while(null != e) {
                MapEntry<K, V> next = e.next;
                int index = getIndex(e.hash,newCapacity);
                //把原来数组中的e放在新的数组中index位置
                newTable[index] = e;
                //将这个e插入到index位置的第一个,将原来index位置的e指定给e的next
                e.next =  newTable[index];
                //把原来数组中某index位置这条链表上的每一个元素都重新归位
                e = next;
            }
        }
    }

    /**
     * 通过hash值找到table的index
     * @param hash
     * @return
     * 
     * & : 两位同时为“1”,结果才为“1”,否则为0
     */ 
    private int getIndex(int hash,int length) {
        return hash & length-1;
    }

    /**
     * ^ : 就是相同为0不同为1
     * @param key
     * @return
     */
    private int hash(K key) {
        int h = 0;
        h = key.hashCode();
        int h16 = h>>>16;
        return (key == null)?0:(h^(h16));
    }

    public V get(K key) {
        if(key == null) {
            return null;
        }
        MapEntry<K, V> entry = getEntry(key);
        return entry == null?null:entry.value;
    }

    private MyHashMap<K, V>.MapEntry<K, V> getEntry(K key) {
        int hash = hash(key);
        int index = getIndex(hash,table.length);
        
        for (MapEntry<K,V> e = table[index]; e!=null;e = e.next) {
            Object k = null;
            if(e.hash == hash && ((k = e.key) == key) || (key.equals(k))){
                return e;
            }
        }
        return null;
    }

    public int getSize() {
        // TODO Auto-generated method stub
        return size;
    }
}
1.6 hashmap源码 hashmap源码,可以看看http://blog.csdn.net/wabiaozia/article/details/50684556 立即下载

相关推荐

从零开始训练一个简单的CLIP

到这里,你已经基本了解了CLIP的基本原理,并且能够自己动手训练一个简单的CLIP模型。整体上来看,代码比较简单,但是已经基本覆盖到CLIP的整体训练流程了。另外,如果需要实际的落地,建议还是使用openclip等更加成熟的框架。欢迎关注github:https://github.com/taishan1994。

u012374012的专栏 2906

【Java】HashMap实现原理分析(源码分析,ReHash,扩容机制)

目录 一、HashMap的数据结构 解决hash冲突的办法 二、源码分析 1.位桶数组 2.数组元素Node实现了Entry接口,v> 3.HashMap如何put(key,value) 4.HashMap如何getValue值 5.HasMap的扩容机制resize(); 6. JDK1.8使用红黑树的改进 三、再谈ReHash 单线程下的ReHash ...

康师傅没有眼泪 4580

Kali Linux下John the Ripper实战:从识别yescrypt哈希到破解zip压缩包(保姆级避坑指南)

本文详细介绍了在Kali Linux中使用John the Ripper工具从识别yescrypt哈希到破解zip压缩包的全过程。针对常见的'No password hashes loaded'报错,提供了两种解决方案,并扩展讲解了哈希识别、zip文件破解及性能优化技巧,是安全测试人员的实用指南。

weixin_29323365的博客 228

Map 综述(三):彻头彻尾理解 ConcurrentHashMap

ConcurrentHashMap是J.U.C的重要成员,它是HashMap的一个线程安全的版本。在默认理想状态下,ConcurrentHashMap可以支持16个线程执行并发写操作及任意数量线程的读操作。本文将结合Java内存模型和JDK源代码,剖析其高并发的具体实现机制,包括在JDK中的定义和结构、并发存取、重哈希和跨段操作,并着重剖析了ConcurrentHashMap读操作不需要加锁的内在奥秘和原理。

Rico's Blogs 14万+

浅谈专项测试之弱网络测试

浅谈专项测试之弱网络测试 一.弱网络测试背景 移动端产品的使用并非完全都是在流畅的wifi环境,大部分用户主要使用4G,3G,2G等网络,另外因为移动端产品使用的场景多变,如进公交,上地铁,坐电梯,使得弱网测试显得尤为重要。考虑到各种场景的客户端展示及容错,提升产品的印象和用户体验,提高产品的健壮性,弱网测试必不可少。 二.弱网络测试建议 网下需要进行功能点测试,无网络测试,网络切换测试等,测试过程除了关注功能点的实现,还应关注用户体验等各个方面 三.弱网络测试通用功能点总结 四.弱网络测试用例实例

ITstudy001的博客 1005

AD服务器搭建

一、角色安装1)打开服务器管理器,添加 角色和功能;2) 点 下一步;3)点 下一步;4)点 下一步;5)点击安装;6-1)将此服务器提升为域控制器;6-2)点下一步;6)输入密码 点 下一步;7)自动生成LIXF,点击下一步;8)确认目录,点下一步;8)确认安装条件,点安装; 8)安装成功,点关闭;...

潇峰的博客 6422

hashmap原理_HashMap底层实现和原理

HashMap简介 概述HashMap基于Map接口实现,元素以键值对的方式存储,并且允许使用null 键和null 值。它的key不允许重复,因此只能有一个键为null。另外,由于HashMap是无序的,因此不能保证放入元素的顺序。同时HashMap是线程不安全的。 继承关系public class HashMap<K,V>extends AbstractMap&l...

weixin_39757893的博客 524

(8)机器人动力学

机器人是一个具有多输入多输出的复杂动力学系统,存在严重的非线性,需要非常系统的方法对机器人进行动力学研究。常用的机器人动力学建模方法有:Lagrange(拉格朗日)动力学方法、Newton-Euler(牛顿-欧拉)动力学方法、Gauss(高斯)动力学方法、Kane(凯恩)动力学方法等。机器人动力学研究的是机器人的运动和作用力之间的关系。机器人的动力学问题包括动力学正问题和动力学逆问题。动力学正问题是对于给定的关节驱动力......

rouyu308的博客 9491

HashMap(openjdk1.6)源码分析

本文只是笔记,如有错误,欢迎指出 openjdk HashMap源码在线查看 HashMap的成员变量 //默认的初始化容量,必须是2的幂次方 static final int DEFAULT_INITIAL_CAPACITY = 16; //最大容量 static final int MAXIMUM_CAPACITY = 1 &amp;amp;amp;amp;lt;&amp;amp;amp;amp;lt; 30; //默认的加载因子 sta...

HelloWorld 349

【Pico手势开发实战】基于SteamVR串流与MRTK3的跨平台交互方案

本文详细介绍了在Pico设备上,结合SteamVR串流与微软MRTK3框架,实现高效跨平台手势交互开发的完整方案。该方案通过在PC端运行Unity项目并串流至头显,利用MRTK3封装的手势识别与交互组件,大幅提升了开发调试效率与性能上限,并确保了未来向其他OpenXR设备的良好兼容性。

weixin_29001683的博客 210

AIGC算法工程师面试八股文深度解析与进阶指南:揭秘Diffusion Models核心原理与实战技巧

总结来说,Cosine Noise Scheduler 的核心优势在于它能够更巧妙地平衡信号和噪声的比例,在扩散过程的早期阶段更长时间地保留原始图像的细节信息,从而为去噪模型提供一个更友好的学习环境,最终带来更优异的生成质量。🚀

FJN110的博客 341

HashMap源码分析(基于JDK1.6)

    在Java集合类中最常用的除了ArrayList外,就是HashMap了。本文尽自己所能,尽量详细的解释HashMap的源码。一山还有一山高,有不足之处请之处,定感谢指定并及时修正。     在看HashMap源码之前先复习一下数据结构。     Java最基本的数据结构有数组和链表。数组的特点是空间连续(大小固定)、寻址迅速,但是插入和删除时需要移动元素,所以查询快,增加删除慢。链表...

weixin_34384557的博客 330

嵌入式面试题精选100道及参考答案(4万字长文)

指针是C语言中一种特殊的变量,它存储的是另一个变量的内存地址。指针的使用提供了一种间接访问和操作内存的方法。地址操作:通过使用运算符,可以获取变量的地址,并将这个地址赋值给指针变量。间接引用:使用运算符可以访问指针指向的变量的值。动态内存分配:指针用于动态地分配和释放内存。数组操作:指针可以用于遍历和操作数组。函数参数:指针作为函数参数可以改变原始数据。字符串操作:在C语言中,字符串通常以字符数组的形式存在,并通过字符指针进行操作。// 指向数组的第一个元素i < 5;++i) {在这个例子中,

大模型大数据攻城狮的专栏 1961

JDK1.8---HashMap的resize()扩容方法源码详解

JDK1.8---HashMap的resize()扩容方法源码详解resize()方法触发时机下面开始根据源码介绍HashMap的扩容原理下面进行分步分析扩容 resize()方法触发时机 首先总结一下JDK1.8的HashMap都在什么时候触发resize()方法,根据阅读源码总结了三个时机触发扩容,这里只做介绍,后面根据源码详细分析 HashMap是由数组+链表+红黑树构成的,数组就称之为桶了...

qq_36856024的博客 4280

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

HashMap 源码详细分析(JDK1.8)

本章对 HashMap 常见操作相关代码进行了详细分析,并在最后补充了一些其他细节。在本章中,插入操作一节的内容说的最多,主要是因为插入操作涉及的点特别多,一环扣一环。包含但不限于“table 初始化、扩容、树化”等,总体来说,插入操作分析起来难度还是很大的。好在,最后分析完了。...

油墨香^-^的博客 622

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

HashMap的jdk版本不同

关于HashMap,其实在jdk1.8和1.7版本之间的差别还是蛮大的,这几天在经过认真分析和理解之后,通过实验将学习成果进行展示。 首先提出一个问题:jdk1.7多线程环境下HashMap容易出现死循环? public class HashMapTest { public static void main(String[] args) { HashMapThread thread0 = new HashMapThread(); HashMapThread t

会飞的猪的博客 520

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

JDK1.8源码阅读系列之四:HashMap (转载)

文章原地址:JDK1.8源码阅读系列之四:HashMap (原创) https://www.cnblogs.com/Michaelwjw/p/6411176.html     本篇随笔主要描述的是我阅读 HashMap 源码期间的对于 HashMap 的一些实现上的个人理解,用于个人备忘,有不对的地方,请指出~   接下来会从以下几个方面介绍 HashMap 源码相关知识:   1、Ha...

houkepei的博客 196

hashmap源码阅读

  接下来会从以下几个方面介绍 HashMap 源码相关知识:  1、HashMap 存储结构  2、HashMap 各常量、成员变量作用  3、HashMap 几种构造方法  4、HashMap put 及其相关方法  5、HashMap get 及其相关方法  6、HashMap remove 及其相关方法(暂未理解透彻)  7、HashMap 扩容方法 resize()  介绍方法时会包含方...

zhoubinwd的专栏 302

面试烂的hashMap你真的懂吗?

前言:HashMap是java工程师在面试过程中,碰到的概率极大的一道题,很大一部分人都是看了一些博客,记住一些答案,但是深究往往无法接招,本文就来扒一扒hashMap面试常见的点及源码分析。 推荐分析目前为止个人看过的分析hashMap比较好的文章。 Java 8系列之重新认识HashMap 没有阅读过的同学,可以先看一下这篇文章,对于hashMap的设计及原理有一个大概的了解,一遍不行;读两遍...

大黄奔跑 310

Java开发者如何高效阅读源码并提升自己

读完源码不等于学会,必须做总结。可以在GitHub上开一个仓库,把阅读过程中的注释、分析、时序图整理记录下来。更进阶的做法是手写一个精简版——比如手写一个微型Spring MVC,自己实现扫描、URL映射和反射调用。这个过程会让你真正理解轮子为什么是圆的,而不仅仅是知道轮子长什么样。阅读源码不是一蹴而就的事,它靠的是日复一日的积累和坚持。当你能够从源码中读出设计者的决策思路和取舍哲学时,你就不再是一个只会调API的熟练工,而是一个真正理解技术、驾驭技术的Java高手。

2501_90410679的博客 7

java 8 Hashmap深入解析 —— put get 方法源码(转载)

https://www.cnblogs.com/jzb-blog/p/6637823.html       每个java程序员都知道,HashMap是java中最重要的集合类之一,也是找工作面试中非常常见的考点,因为HashMap的实现本身确实蕴含了很多精妙的代码设计。   对于普通的程序员,可能仅仅能说出HashMap线程不安全,允许key、value为null,以及不要求线程安全时,效率上比HashTable要快一些。稍微好一些的,会对具体实现有过大概了解,能说出Ha.

hello 266

java 8 Hashmap深入解析 —— put get 方法源码

本文为原创博文,转载请注明出处,侵权必究! 每个java程序员都知道,HashMap是java中最重要的集合类之一,也是找工作面试中非常常见的考点,因为HashMap的实现本身确实蕴含了很多精妙的代码设计。   对于普通的程序员,可能仅仅能说出HashMap线程不安全,允许key、value为null,以及不要求线程安全时,效率上比HashTable要快一些。...

249

基于jdk1.6的ArrayList源码分析(温故学习)

鼎力推荐一下  杭州Mark的HashMap源码分析ArrayList源码分析(基于JDK1.6)    《Java集合类》中讲述了ArrayList的基础使用,本文将深入剖析ArrayList的内部结构及实现原理,以便更好的、更高效的使用它。    ArrayList就是传说中的动态数组,就是Array的复杂版本,它提供了如下一些好处:动态的增加和减少元素、灵活的设置数组的大小......   ...

ROCK 387

jdk源码分析--0.阅读建议

本文转载自 https://blog.csdn.net/qq_21033663/article/details/79571506 标题为包名,后面序号为优先级1-4,优先级递减 1、java.lang 1) Object 1 2) String 1 3) AbstractStringBuilder 1 4) StringBuffer 1 5) StringBuilder 1 6) Boole...

weijiechenlun133的博客 193

java 源码_推荐最全源码分析Java七大源码框架一次彻底搞懂

实力宠粉,为了感谢支持我的朋友!整理了一份Java高级架构资料~~阅读源码框架如下:Mybatis、Spring、Redis、Java并发、Dubbo、RocketMq、Sharding-sphere等作为一名优秀的 Java 开发程序员,以及想那些想要学习 Java 更深层一点的知识的同学,对源码框架的熟悉与熟练阅读与使用是必不可缺的核心技能了,也是每个 Java 程序员应该要做到的。比如去面试...

weixin_39846361的博客 1668
上一篇: 第十五章:交互式界面(十三)
下一篇: 细数 JavaScript 实用黑科技(一)
weixin_34232744
博客等级 码龄11年 5896粉丝 1151原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值