1、HashMap的数据结构(HashMap通过hashcode对其内容进行高速查找,是无序的)



数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端。

数组 :数组的存储区是连续的,占用内存严重,故空间复杂度非常大。但数组的二分查找时间度小;数组的特点:寻址easy,插入和

删除困难。

链表 :链表的储存区离散。占用内存比較宽松。故空间复杂度非常小,但时间复杂度大;链表的特点:寻址困难,插入和删除easy。

哈希表

HashMap是由数组+链表组成。寻址easy,插入和删除easy。(存储单元数组Entry[],数组里面包括链表

HashMap事实上也是由一个线性的数组实现的。

所以能够理解为其存储数据的容器就是一个线性容器;

HashMap里面有一个内部静态类Entry,其重要的属性有key,value,next,从属性key,value 就能够非常明显的看出来 Entry就是

HashMap键值对实现的一个基础bean;也就是说HashMap的基础就是一个线性数组,这个数组就是Entry[]。Map里面的内容都保存

在Entry[]中;

    /**
* The table, resized as necessary. Length MUST Always be a power of two.
*/ transient Entry[] table;

2、HashMap的存取实现



2.1:存储

这里HashMap用了一个算法。

//存储时候:

int hash=key.hashCode(); //获取key的hashCode,这个值是一个固定的int值

int index=hash%Entry[].length。//获取数组下标:key的hash值对Entry数组长度进行取余

Entry[index]=value。

注意:假设两个key通过hash%Entry[].length得到的index同样。会不会覆盖?

是不会的。Entry类有一个next属性,作用是指向下一个Entry。打个例如, 第一个键值对A进来。通过计算其key的hash得到的

index=0。记做:Entry[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,如今怎么办?HashMap会这样做:B.next =

A,Entry[0] = B,假设又进来C,index也等于0,那么C.next = B,Entry[0] = C;这样我们发现index=0的地方事实上存取了A,B,C三个键值对,他

们通过next这个属性链接在一起。

所以疑问不用操心。

也就是说Entry[]数组中存储的是最后插入的数据

	 public V put(K key, V value) {
if (key == null)
return putForNullKey(value); //null总是放在数组的第一个链表中
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;
//假设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;
}
}
modCount++;
addEntry(hash, key, value, i);
return 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); //參数e, 是Entry.next
//假设size超过threshold,则扩充table大小。再散列
if (size++ >= threshold)
resize(2 * table.length);
}

2.2:取值

获取key的hashcode指,通过hash值去hash%Entry[].length  获取Entry[hash%Entry[].length],定位到该数组元素之后,再遍历该元

素处的链表。

//取值时候:

int hash=key.hashCode();

int index =hash%Entry[].length;

return Entry[index];

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

      当哈希表的容量超过默认容量时,必需要调整table的大小。

当容量达到最大值时,该方法Integer.MAX_VALUE返回。这时。就需要创建

一张表,将原来的表映射到新表中。



3、HashMap、HashTable和ConcurrentHashMap的线程安全问题

HashMap:线程不安全的。

HashTable:锁住整张hash表,让线程独占。hashMap同意为空。

通过分析Hashtable就知道,synchronized是针对整张Hash表的,即每次锁住整张表

让线程独占。安全的背后是巨大的浪费。

ConcurrentHashMap:一个更快的hashmap,它提供了好得多的并发性。多个读操作差点儿总能够并发地运行。

他是锁段(默认:把hash表分为16个

),在get,put,remove等操作中,ConcurrentHashMap仅仅锁定当前须要用到的段,仅仅有在求size的时候才锁定整张hash表。

HashMap的存储结构及原理的更多相关文章

  1. 牛客网Java刷题知识点之HashMap的实现原理、HashMap的存储结构、HashMap在JDK1.6、JDK1.7、JDK1.8之间的差异以及带来的性能影响

    不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑          ...

  2. 扰动函数和拉链法模拟HashMap的存储结构

    HashMap是Map接口下面的子孙,它对外是K,V结构存储的,而内部也着自己的存储结构,它的get操作是O(1)的时间复杂度,可以说是非常快的找到目录,而添加时,也是O(1),所以在键值存储里,它成 ...

  3. hbase操作(shell 命令,如建表,清空表,增删改查)以及 hbase表存储结构和原理

    两篇讲的不错文章 http://www.cnblogs.com/nexiyi/p/hbase_shell.html http://blog.csdn.net/u010967382/article/de ...

  4. HashMap的底层结构和原理

    http://youzhixueyuan.com/the-underlying-structure-and-principle-of-hashmap.html HashMap是Java程序员使用频率最 ...

  5. hbase 存储结构和原理

    HBase的表结构 建表时要指定的是:表名.列族 建表语句 create 'user_info', 'base_info', 'ext_info' 意思是新建一个表,名称是user_info,包含两个 ...

  6. Map实现之HashMap(结构及原理)(转)

    java.util包中的集合类包含 Java 中某些最常用的类.最常用的集合类是 List 和 Map.List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构 ...

  7. HashMap的存储原理

    HashMap是java中相当重要的数据结构,使用HashMap的场景非常之多,因此,了解HashMap实现的过程和原理,是非常有必要的,在一些面试中也会经常被问到.好了,我们赶紧来研究java内部是 ...

  8. HashMap底层结构、原理、扩容机制

    https://www.jianshu.com/p/c1b616ff1130 http://youzhixueyuan.com/the-underlying-structure-and-princip ...

  9. Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构

    Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构 1. 索引的分类1 1.1. 按照存储结构划分btree,hash,bitmap,fulltext1 1.2. 索引的类型  按查找 ...

随机推荐

  1. linux添加JAVA环境变量

    root用户: 1.修改文件vim /etc/profile 添加以下信息: export JAVA_HOME=/home/jdk1..0_79 (这里需要添加自己的JDK安装目录) export C ...

  2. vim+ctags+cscope工具

    最近在看一个开源的项目,突然发现在Linux上不知道有什么方便的工具来看开源代码.以前,在Windows上用VS2010集成开发环境,对于任意一个函数直接有快捷键跳转到函数定义处.可现在在Linux上 ...

  3. uva 10922 - 2 the 9s

    題目意思:讀取一數字,此數字最大有1000位.計算該數字是否為九的倍數?如是,再計算其階層數. ※判斷是否為九的倍數:所有位數相加 ÷ 9=0,即為九的倍數. ※計算階層數:所有位數相加後得出的第一個 ...

  4. ajax初学

    //参数说明url:请求文件地址//fnSucc:请求成功执行的函数,请求成功的条件为readyState状态码为4:且status状态码为200,status状态为浏览器请求返回的状态码//在fnS ...

  5. ThinkPHP内置函数详解D、F、S、C、L、A、I

    单字母函数D.F.S.C.L.A.I 他们都在ThinkPHP核心的ThinkPHP/Mode/Api/functions.php这个文件中定义. 下面我分别说明一下他们的功能: D() 加载Mode ...

  6. [算法]分治算法(Divide and Conquer)

    转载请注明:http://www.cnblogs.com/StartoverX/p/4575744.html 分治算法 在计算机科学中,分治法是建基于多项分支递归的一种很重要的算法范式.字面上的解释是 ...

  7. django 模板视图,表单视图,各种视图

    Generic editing views¶ The following views are described on this page and provide a foundation for e ...

  8. production stage

    MP:mass production 批量生产 DV design verification 设计验证 EV engineering verification 工程样品验证 PV process ve ...

  9. Gradient Descent 和 Stochastic Gradient Descent(随机梯度下降法)

    Gradient Descent(Batch Gradient)也就是梯度下降法是一种常用的的寻找局域最小值的方法.其主要思想就是计算当前位置的梯度,取梯度反方向并结合合适步长使其向最小值移动.通过柯 ...

  10. AFNetworking 2.0 新特性讲解之AFHTTPSessionManager

    AFNetworking 2.0 新特性讲解之AFHTTPSessionManager (2014-02-17 11:56:24) 转载▼     AFNetworking 2.0 相比1.0 API ...