先来看看HashMap的一些成员变量以及他们的含义

  1. /**
  2. * The default initial capacity - MUST be a power of two.
  3. */
  4. static final int DEFAULT_INITIAL_CAPACITY = 16;//默认Entry数组的长度
  5.  
  6. /**
  7. * The load factor used when none specified in constructor.
  8. */
  9. static final float DEFAULT_LOAD_FACTOR = 0.75f;//加载因子,和数组长度有关
  10.  
  11. /**
  12. * The table, resized as necessary. Length MUST Always be a power of two.
  13. */
  14. transient Entry<K,V>[] table;//HashMap底层实现就是Entry数组,以后说的数组就是这个数组
  15.  
  16. /**
  17. * The number of key-value mappings contained in this map.
  18. */
  19. transient int size; //添加元素的个数

HashSet的底层实现就是用HashMap实现的。

hashset的add方法就是用hashmap的put方法。key是添加的元素,value是hashset维护的一个常量。这里不多说了,主要还是分析HashMap的源代码。

  1. public V put(K key, V value) {
  2. if (key == null)
  3. return putForNullKey(value);//添加键为null的元素
  4. int hash = hash(key);//得到key的hash值,这个hash值是对key的hashcode进行过处理的
  5. int i = indexFor(hash, table.length);//根据hash值和数组长度得到元素保存到数组中的位置
  6. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  7. Object k;
  8. //判断将要添加元素的位置是否有元素了
  9. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  10. //根据hashcode和equals方法判断键是否重复了,如果键重复了,将值替换了,并返回原来的值。
  11. V oldValue = e.value;
  12. e.value = value;
  13. e.recordAccess(this);
  14. return oldValue;
  15. }
  16. }
  17. //如果键不重复,则添加到数组中
  18. modCount++;
  19. addEntry(hash, key, value, i);//hash值,键,值,插入到数组的位置(索引)
  20. return null;
  21. }
  1. /**
  2. * Offloaded version of put for null keys
  3. */
  4. private V putForNullKey(V value) {
  5. for (Entry<K,V> e = table[0]; e != null; e = e.next) {
  6. if (e.key == null) {//添加的key为null,如果已经存在key为null的元素,则替换原来的值,返回旧值
  7. V oldValue = e.value;
  8. e.value = value;
  9. e.recordAccess(this);
  10. return oldValue;
  11. }
  12. }
  13. modCount++;
  14. addEntry(0, null, value, 0);//添加第一个key为null的元素
  15. return null;
  16. }
  17.  
  18. //可以看出,添加key为null的元素每次都是添加到数组的第一个位置,重复添加key为null的元素,新值替换旧值
  1. void addEntry(int hash, K key, V value, int bucketIndex) {
  2. if ((size >= threshold) && (null != table[bucketIndex])) {
  3. resize(2 * table.length);//如果元素个数大于threshold(这个数是一个数乘以加载因子,与加载因子相乘的这个数与初始化数组长度有关) 扩容为原来的两倍
  4. hash = (null != key) ? hash(key) : 0;
  5. bucketIndex = indexFor(hash, table.length);//得到一个新的位置
  6. }
  7.  
  8. createEntry(hash, key, value, bucketIndex);
  9. }
  1. void createEntry(int hash, K key, V value, int bucketIndex) {
  2. Entry<K,V> e = table[bucketIndex];//得到存在数组中该位置的元素
  3. table[bucketIndex] = new Entry<>(hash, key, value, e);//将新添加的元素存到该位置上,并关联之前保存在该位置上的数组
  4. size++;//元素个数+1
  5. }
  1. static class Entry<K,V> implements Map.Entry<K,V> {
  2. final K key;
  3. V value;
  4. Entry<K,V> next;
  5. int hash;
  6.  
  7. /**
  8. * Creates new entry.
  9. */
  10. Entry(int h, K k, V v, Entry<K,V> n) {
  11. value = v;
  12. next = n;
  13. key = k;
  14. hash = h;
  15. }
  16. ...
  17. }
  18. //Entry的结构

总结:当添加key为null的元素,会保存在数组的第一个位置上。其他key不为null的情况,是根据key的hash值来判断保存都Entry数组中的位置,如果该位置上没有元素,则直接添加。如果数组中已经在该位置上有元素了,根据hashcode和equals方法来判断键是否重复,如果重复,则替换旧值,并返回旧值。如果不重复,则将新元素添加到数组的索引上,然后通过Entry的next属性关联之前保存在该位置上的元素。当元素的个数大于某个值(不作详解)且该位置上有元素时,数组长度扩容为原来的两倍。

上面是添加元素,下面是取元素。

  1. public V get(Object key) {
  2. if (key == null)
  3. return getForNullKey();//获取键为null的值
  4. Entry<K,V> entry = getEntry(key);//根据key获取元素
  5.  
  6. return null == entry ? null : entry.getValue();
  7. }
  1. private V getForNullKey() {
  2. for (Entry<K,V> e = table[0]; e != null; e = e.next) {
  3. if (e.key == null)
  4. return e.value;//返回key为null的值
  5. }
  6. return null;//如果不存在key为null,则返回null
  7. }
  1. final Entry<K,V> getEntry(Object key) {
  2. int hash = (key == null) ? 0 : hash(key);//得到key的hash值
  3. for (Entry<K,V> e = table[indexFor(hash, table.length)];
  4. e != null;
  5. e = e.next) {//根据key的hash值来索引保存在数组中的位置,如果索引到的位置有元素,判断key是否一致,直到判断完该位置上的所有元素,如果都不匹配,则返回null。如果该位置上没有元素,则返回null。
  6. Object k;
  7. if (e.hash == hash &&
  8. ((k = e.key) == key || (key != null && key.equals(k))))
  9. return e;
  10. }
  11. return null;//不存在key则返回null
  12. }

jdk7 HashSet和HashMap源码分析的更多相关文章

  1. HashSet底层HashMap源码分析

    在看HashSet源码的时候,意外发现底层HashMap保存的value居然不是null,而是保存一个Object作为Value.顿觉有悖常理,于是来分析一下: HashSet的add方法: publ ...

  2. Java HashMap源码分析(含散列表、红黑树、扰动函数等重点问题分析)

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  3. HashMap源码分析和应用实例的介绍

    1.HashMap介绍 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射.HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.S ...

  4. Java HashSet和HashMap源码剖析

    转自: Java HashSet和HashMap源码剖析 总体介绍 之所以把HashSet和HashMap放在一起讲解,是因为二者在Java里有着相同的实现,前者仅仅是对后者做了一层包装,也就是说Ha ...

  5. 【JAVA集合】HashMap源码分析(转载)

    原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储 ...

  6. Java中HashMap源码分析

    一.HashMap概述 HashMap基于哈希表的Map接口的实现.此实现提供所有可选的映射操作,并允许使用null值和null键.(除了不同步和允许使用null之外,HashMap类与Hashtab ...

  7. JDK1.8 HashMap源码分析

      一.HashMap概述 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里.但是当位于一个桶中的元素较多,即hash值相等的元素较多时 ...

  8. 【Java】HashMap源码分析——常用方法详解

    上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...

  9. 【Java】HashMap源码分析——基本概念

    在JDK1.8后,对HashMap源码进行了更改,引入了红黑树.在这之前,HashMap实际上就是就是数组+链表的结构,由于HashMap是一张哈希表,其会产生哈希冲突,为了解决哈希冲突,HashMa ...

随机推荐

  1. 关于MEF

    MEF(Managed Extensibility Framework)是.NET Framework 4.0一个重要的库,Visual Studio 2010 Code Editor的扩展支持也是基 ...

  2. 删除重复&海量数据

    08. 删除重复&海量数据   重复数据,通常有两种:一是完全重复的记录,也就是所有字段的值都一样:二是部分字段值重复的记录. 一. 删除完全重复的记录完全重复的数据,通常是由于没有设置主键/ ...

  3. web前端安全---读书笔记

    web前端安全---读书笔记 粗略的看完了Web前端黑客技术揭秘前两章了,由于自身的前端功力不深,当然也是初涉前端的安全问题,所以实话还是有些问题看不太明白的.在豆瓣看到的这本书,名字真心有点很肥主流 ...

  4. rabbitmq在mac上安装

    1.安装brew 打开http://bash.sh  执行 ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/ ...

  5. Ubuntu 下的环境变量配置

    网上很多配置jdk环境变量的方法,但是几乎都会下次重启电脑就失效,或者时不时的失效.下面教你一招 JDK环境变量配置如下: 执行命令sudo gedit /etc/environment,在打开的编辑 ...

  6. [置顶] vs2008 编译adb 支持4.2 android 系统(改进版)

    QQ: 2506314894 本想晚些时候放出来的,但是按捺不住啊,所以修改了之后就立即放出来了.先说明一下,这次用的adb 的源码比较新的,用的vs2008 编译出来,只有一个exe 文件,直接就可 ...

  7. hdu 4515 年月份模拟题

    小Q系列故事——世界上最遥远的距离 Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) ...

  8. .Net程序员学用Oracle系列(7):视图、函数、过程、包

    <.Net程序员学用Oracle系列:导航目录> 本文大纲 1.视图 1.1.创建视图 2.函数 2.1.创建函数 2.2.调用函数 3.过程 3.1.创建过程 3.2.调用过程 4.包 ...

  9. 使用TypeScript开发ReactNative应用的简单示例

    最近小小尝试了下 ReactNative + TypeScript 开发APP,爬了无数坑之后总算弄出来个结果,重要的地方记录下,后面会附上示例代码: 1.开发工具的选择 windows 平台我接触的 ...

  10. 一键强制修改任意Mysql数据库的密码,修改任意环境Mysql数据库。

    本文采用我软件里面的内置改密功能,可以一键强制修改Mysql数据库的密码, 在修改过程中,会强制干掉Mysql主程序,修改完成后重新启动Mysql就可以了. 首先讲解如何一键强制修改PHPWAMP自身 ...