这个这个。。。本王最近由于开始找实习工作了,所以就在牛客网上刷一些公司的面试题,大多都是一些java,前端HTML,js,jquery,以及一些好久没有碰的算法题,说实话,有点难受,其实在我不知道的很多是地方还有很多很多的知识漏洞,就像这一次写的这个,也是我在刷题的时候感觉到真的是我空缺的地方,为什么呢?因为,做多了,错多了。然而很尴尬的又是因为这个只是也是很多公司的面试题,所以索性直接写下来整理一遍。

在这里我也建议各位,牛客网不仅仅是一个找工作的station也是一个可以锻炼我们的地方,没事刷刷题啊,逛逛论坛啊,说不定就能找到很多你意想不到的东西。

在面试的过程中,有几个问题是比较常见的。

  1. HashTable、HashMap、ConcurrentHashMap的区别?
  2. HashMap线程不安全的出现场景?
  3. HashMap put方法存放数据时是怎么判断是否重复的? 
  4. JDK7和JDK8 中HashMap的实现有什么区别?
  5. HashMap的长度为什么是2的幂次方?

只要把这几个问题过一遍之后,大致了解了他们各自的作用与互相之间的区别再!!去敲一遍其实就可以掌握了。

放一张珍贵的图给大家学习学习!

HashTable

  • 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
  • 初始size为11,扩容:newsize = oldsize*2+1
  • 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

HashMap

  • 在底层数组+链表中实现,线程不安全,可存储null键和null值
  • 初始size为16,可扩容:newsize = oldsize*2,size一定为2的n次幂
  • 扩容针对整个Map,每次扩容的时候,原来数组中的元素依次重新计算存放的位置,并重新插入
  • 插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
  • 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
  • 计算index方法:index = hash & (tab.length – 1)

*HashMap的初始值还要考虑加载因子:
哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链,对Key的查找需要遍历Entry链上的每个元素执行equals()比较。
加载因子:为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。因此,如果预估容量是100,即需要设定100/0.75=134的数组大小。
空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。

HashMap与HashTable的区别(面试题常考~)

1.两者所继承的父类不同

HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。

在这里原本是截取了JDK API1.6 中文版里面的,但实在是太丑了,就在别人的博客,呵呵,悄咪咪的拿了过来借鉴了一下

2.两者对外接口是不同的

HashTable比HashMap多提供了elements()和contains()两个方法。

elements()方法继承自HashTable的父类Doctionnary。elements()方法用于返回此时HashTable中的值的枚举。

contains()方法判断该Hashtable是否包含传入的value。它的作用与containsValue()一致。事实上,contansValue() 就只是调用了一下contains() 方法,是判断哈希表中是否包含指定的值。如图是contains的源码:

public virtual bool Contains(object key)
{
return this.ContainsKey(key);
}

3.对Null key 和Null value的支持不同
Hashtable既不支持Null key也不支持Null value。

HashMap中,key-value都是存在Entry中的。null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null,不保证元素的顺序恒久不变,它的底层使用的是数组和链表,用过HashCode()方法和equal()方法来保证键的唯一性。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
4.线程安全的不同性

Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步。

HashMap不是线程安全的,在多线程并发的环境下,可能会产生死锁等问题。所以使用HashMap时就必须要自己增加同步处理,

虽然HashMap不是线程安全的,但是它的效率会比Hashtable要好很多。这样设计是合理的。在我们的日常使用当中,大部分时间是单线程操作的。HashMap把这部分操作解放出来了。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
5.Hash值的计算方法不同

为了求得元素的位置,需要根据元素的Key计算出一个哈希值,然后再用这个哈希值来计算出崔忠的位置。

Hashtable直接使用对象的hashCode。hashCode是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。然后再使用除留余数发来获得最终的位置。

Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的。

HashMap为了提高计算效率,将哈希表的大小固定为了2的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。

HashMap的效率虽然提高了,但是hash冲突却也增加了。因为它得出的hash值的低位相同的概率比较高,而计算位运算

为了解决这个问题,HashMap重新根据hashcode计算hash值后,又对hash值做了一些运算来打散数据。使得取得的位置更加分散,从而减少了hash冲突。当然了,为了高效,HashMap只做了一些简单的位处理。从而不至于把使用2 的幂次方带来的效率提升给抵消掉。

ConcurrentHashMap

  • 底层采用分段的数组+链表实现,线程安全。
  • key和value都不能为null。
  • 通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。
  • Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
  • 有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
  • 扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容

这个就很棒了,上面总结的源于某猿大神,Java5提供的ConcurrentHashMap就像是HashTable的升级版,扩容性更强。

在HashMap中,通过get()返回的null值,既可以表示返回该Key所对应过的Value是null值,也可以表示为没有该Key,在这种情况下就应该采用ConcurrentHashMap。

来看一张简单的类图:

ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一个可重入锁(ReentrantLock),在ConcurrentHashMap里扮演锁的角色;HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组。Segment的结构和HashMap类似,是一种数组和链表结构。一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素。当对HashEntry数组的数据进行修改时,必须首先获得与它对应的segment锁。

Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。

简单理解就是,ConcurrentHashMap是一个Segment数组,Segment通过继承ReentrantLock来进行加锁,所以每次需要加锁的操作锁住的是一个Segment,只要保证每个Segment是线程安全的,也就实现了全局的线程安全。重申一下,Segment数组不能扩容,扩容是Segment数组某个位置内部的数组HashEntry<K,V>[]进行扩容,扩容后,容量为原来的2倍。可以回顾下出发扩容的地方,put的时候,如果判断该值的插入会导致该Segment的元素个数超过阈值,那么先进行扩容,再插值。

TreeMap

  • TreeMap实现了NavigableMap接口,而Navigable接口继承着继承着SortedMap接口,致使我们的TreeMap是有序的
  • TreeMap底层是红黑树,它方法时间复杂度都不会太高:log(n)~
  • 非同步
  • 使用Comparator或者Comparable来比较key是否相等与排序的问题~
  • key不可以为null,会报NullPointerException异常,value可以为null。
  • TreeMap中的元素默认按照keys的自然排序排列。(对Integer来说,其自然排序就是数字的升序;对String来说,其自然排序就是按照字母表排序)

HashMap、Hash Table、ConcurrentHashMap的更多相关文章

  1. Java 总结 数据底层原理 【包括 ArrayList、LinkedList、hash table、HashMap、Hashtable、ConcurrentHashMap、hash code、HashSet、LinkedHashMap、LinkedHashSet】

    1.ArrayList (1)底层是由动态数组实现的[使用了List接口]. (2)动态数组是长度不固定,随着数据的增多而变长. (3)如果不指定,默认长度为10,当添加的元素超过当前数组的长度时,会 ...

  2. 探究公钥、私钥、对称加密、非对称加密、hash加密、数字签名、数字证书、CA认证、https它们究竟是什么,它们分别解决了通信过程的哪些问题。

    一.准备 1. 角色:小白.美美.小黑. 2. 剧情:小白和美美在谈恋爱:小黑对美美求而不得.心生怨念,所以从中作梗. 3. 需求:小白要与美美需通过网络进行通信,联络感情,所以必须保证通信的安全性. ...

  3. Nested loops、Hash join、Sort merge join(三种连接类型原理、使用要点)

    nested loop 嵌套循环(原理):oracle从较小结果集(驱动表.也可以被称为outer)中读取一行,然后和较大结果集(被侦查表,也可以叫做inner)中的所有数据逐条进行比较(也是等值连接 ...

  4. 文本表征:SoW、BoW、TF-IDF、Hash Trick、doc2vec、DBoW、DM

    原文地址:https://www.jianshu.com/p/2f2d5d5e03f8 一.文本特征 (一)基本文本特征提取 词语数量 常,负面情绪评论含有的词语数量比正面情绪评论更多. 字符数量 常 ...

  5. B+索引、Hash索引、数据类型长度

    1.为什么在数据库中要用B树索引而不是Hash索引? Mysql Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这 ...

  6. 词向量:part 1 WordNet、SoW、BoW、TF-IDF、Hash Trick、共现矩阵、SVD

    1.基于知识的表征 如WordNet(图1-1),包含同义词集(synonym sets)和上位词(hypernyms,is a关系). 存在的问题: 作为资源来说是好的,但是它失去了词间的细微差别, ...

  7. ( 转 ) 数据库BTree索引、Hash索引、Bitmap位图索引的优缺点

    测试于:MySQL 5.5.25 当前测试的版本是Mysql 5.5.25只有BTree和Hash两种索引类型,默认为BTree.Oracle或其他类型数据库中会有Bitmap索引(位图索引),这里作 ...

  8. HASH、HASH函数、HASH算法的通俗理解

    之前经常遇到hash函数或者经常用到hash函数,但是hash到底是什么?或者hash函数到底是什么?却很少去考虑.最近同学去面试被问到这个问题,自己看文章也看到hash的问题.遂较为细致的追究了一番 ...

  9. 元祖、hash了解、字典、集合

    元祖: 元组跟列表差不多,也是存一组数,只是它一旦创建,便不能再修改,所以又叫只读列表. 创建: names = ('neo', 'mike', 'eric') 特性: # 1.可存放多个值 # 2. ...

随机推荐

  1. 线性模型之LDA和PCA推导

    线性模型之LDA和PCA 线性判别分析LDA LDA是一种无监督学习的降维技术. 思想:投影后类内方差最小,类间方差最大,即期望同类实例投影后的协方差尽可能小,异类实例的投影后的类中心距离尽量大. 二 ...

  2. SLAM方向国内有哪些优秀公司?

    计算机视觉life为读者整理了国内几十家涉及SLAM的优秀公司,涵盖自动驾驶.仓储机器人.服务机器人.无人机.AR.芯片相机等领域. 一 自动/辅助驾驶: 1.百度: 主要产品:自动驾驶软件 百度智能 ...

  3. Jmh测试JDK,CGLIB,JAVASSIST动态代理方式的性能

    前言 JDK,CGLIB,JAVASSIST是常用的动态代理方式. JDK动态代理仅能对具有接口的类进行代理. CGLIB动态代理方式的目标类可以没有接口. Javassist是一个开源的分析.编辑和 ...

  4. Markdown下,上传图片问题

    最简单的方法: 1,登录qq 2,登录博客园,并打开博客园添加随笔的地方:如图: 3,选择需要截屏的地方,按住ctrl+alt+A截屏,然后在qq的发送栏内贴过去 4,鼠标左键按住不松开,然后拖到这里 ...

  5. MySql中的SHOW INDEX 查出的结果列代表的意义

    MySQL SHOW INDEX语法的实际操作用法以及其实际查看索引状态(语法)的具体内容的描述,如果你对这一技术,心存好奇的话,以下的文章将会揭开它的神秘面纱. INDEX FROM tbl_nam ...

  6. python 多个装饰器的调用顺序

    python 多个装饰器的调用顺序 一般情况下,在函数中可以使用一个装饰器,但是有时也会有两个或两个以上的装饰器.多个装饰器装饰的顺序是从里到外(就近原则),而调用的顺序是从外到里(就远原则). 原代 ...

  7. HBase学习笔记一

    HBase简介 HBase概念 HBase的原型是谷歌的Bigtable论文 HBase是一个高可靠性.高性能.面向列.可伸缩的分布式存储系统,利用HBase技术可在廉价PC上搭建起大规模结构化存储集 ...

  8. 多线程总结-同步之synchronized关键字

    目录 1.为什么要使用synchronized? 2.synchronized锁什么,加锁的目的是什么? 3.代码示例 3.1锁this和临界资源对象 3.2锁class类对象 3.3 什么时候锁临界 ...

  9. Element-ui DatePicker显示周数

    1.场景描述 我们公司是做电商的,运营的工作指标都是按周来定的,所以他们对周特别敏感,希望我们能在日期选择器上显示周数.刚接到这个需求时,心中很不乐意,因为Element-ui的日期选择器根本不支持显 ...

  10. java学习笔记(基础篇)--java关键字与数据类型

    java关键字与数据类型 Java语言的关键字是程序代码中的特殊字符.包括: . 类和接口的声明--class, extends, implements, interface . 包引入和包声明--i ...