【Java面试集合】hashmap底层原理

1.什么是集合

集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection()分别是java.util.List和java.util.Set。其中,List的特点是元素有序、元素可重复。Set的特点是元素无序,而且不可重复。List接口的主要实现类有java.util.ArrayList和java.util.LinkedList,Set接口的主要实现类有java.util.HashSet和java.util.TreeSet)和双列集合java.util.Map

2.单例集合

ArrayList ( ArrayList是List接口的一个实现类,在它内部封装了一个长度可变的数组对象。其大部分的方法是从其父类中继承过来的。)。

3.为什么说ArraayList是线程不安全的?

首先说一下什么是线程不安全:线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。 如图,List接口下面有两个实现,一个是ArrayList,另外一个是vector。 从源码的角度来看,因为Vector的方法前加了,synchronized 关键字,也就是同步的意思,sun公司希望Vector是线程安全的,而希望arraylist是高效的,缺点就是另外的优点。 说下原理(百度的,很好理解): 一个 ArrayList ,在添加一个元素的时候,它可能会有两步来完成:

  1. 在 Items[Size] 的位置存放此元素;
  2. 增大 Size 的值。
    在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
    而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。
    那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。

4.ArrayList和LinkedList实现原理:

arraylist数组实现,查询快,增删慢
因为ArrayList底层维护了object[]用于存储对象,如果指定容量不够,容量自动自动增长到原来1.5倍,数组是可以直接按索引查找的,所以查找快,每当在增删时牵扯到数组增容,所以比较慢。
ArrayList的最大容量?
一般来说都是new ArrayList()那么这种构造方法的初始化容量是10,元素超过10,重新分配内存空间,使数组大小增长为16;如果new ArrayList(4)那么他的初始化容量就是4,重新分配增长为7
那么容量变化的规则是什么呢?请看下面的公式:
((旧容量 * 3) / 2) + 1

LinkedList链表实现,增删快,查询慢,
数据的保存需要上一个元素记住下一个元素,查找从头开始查找,一个一个遍历,效率较低速度慢,增加时前一个元素记住自己就行,删除时前一个元素记住后一个元素就行,增删快

5.双例集合

使用hashmap( 集合存储自定义对象做键,要重写hashCode()方法和equals()方法,因为如果用自定义对象当做键的话,要保持元素的唯一性(因为键是唯一的)。)
HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。
哈希冲突解决:链地址法,开放地址法:如果哈希表中两个数据想要存储到一个数组中,把第一个存入,第二个或第三个或后面的空数组中存储,查找也是如此

6.你为什么要用到hashmap?

hashmap可以接受null键值,hashmap是非synchronized,hashmap很快,以及hashmap储存的是键值对。
你知道hashmap的工作原理吗? 你知道hashmap的get()方法的工作原理吗?
hashmap是基于hashing的原理,我们使用put进行存储到hashmap,使用get从hashmap中获取对象。当我们给put方法传递键和值时,我们先对键调用hashcode方法,返回hashcode用于找到bucket位置来存储Entry(存储的键值对叫做entry对象)对象。
当两个对象的hashcode相同会发生什么?
如果存储对象相同,hashmap会抛出异常或者不会存储他们,可以重写hashcode方法和equals两个方法,因为hashcode相同,所以他们的bucket位置相同,碰撞会发生。因为hashmap使用链表存储对象,这个Entry会存储在链表中。
如果两个键的hashcode相同,你如何获取值对象?
当我们调用get()方法,hashmap会使用键对象hashcode找到bucket位置,调用keys,equals方法找到链表中正确的节点,最终找到要找的对象。
如果hashmap的大小超出负载因子定义的容量,怎么办?
默认的负载因子大小是0.75,也就是说,当一个map填满了75%的bucket的时候,和其他集合类(如ArrayList等)一样,将会创建原来hashmap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入到新的bucket数组中,这个过程叫hashing,因为它调用hash方法找到新的bucket位置。
你了解重新调整hashmap大小存在什么问题吗?
当多线程的情况下,可能会产生条件竞争。( 因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了 )
我们可以使用CocurrentHashMap来代替Hashtable吗?
这是另外一个很热门的面试题,因为ConcurrentHashMap越来越多人用了。我们知道Hashtable是synchronized的,但是ConcurrentHashMap同步性能更好,因为它仅仅根据同步级别对map的一部分进行上锁。ConcurrentHashMap当然可以代替HashTable,但是HashTable提供更强的线程安全性。

7.总结:

HashMap基于hashing原理,无序,不可重复。我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
因为HashMap的好处非常多,我曾经在电子商务的应用中使用HashMap作为缓存。因为金融领域非常多的运用Java,也出于性能的考虑,我们会经常用到HashMap和ConcurrentHashMap。
解决hash碰撞问题:
1. 开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
2. 再哈希法
3. 链地址法
4. 建立一个公共溢出区

8.系统学习hashmap底层源码实现

8.1.为什么我看不懂这段源码

【Java面试集合】hashmap底层原理_第1张图片
hashmap是存储键值对的 存储数据的
计算机中如果要存储数据的话,我该怎么办?
数据在计算机中存储的一个方式/数据结构
hashmap底层数据结构搞清楚,我应该可以看懂这段源码
数据结构? 数组,链表,树形,图形结构
不能对这些常见的数据结构对号入座
数组-----》arraylist
链表-----》linkedlist看下源码就知道:找到linkedlist下的add方法,找到Node内部类维护的点,是使用的双向链表
图解hashmap的数据结构 jdk1.8 数组+链表+红黑树
【Java面试集合】hashmap底层原理_第2张图片

8.2.put存储运作方式及流程:

在map.put(1,“jack”);的时候,
1.因为链表有一个node内部类(称为节点),然后生成一个node里边属性有key,value , node next,把这个节点放在数组中,当数组不够用时怎么去扩大?
扩大标准:
感性:如果说数组长度是16,是在16这个节点用完再扩大?
2.理性:在这个数组使用到0.75的时候去扩大,16x0.75=12
3.数组可以无限扩大?不可以的
4…但是链表可以无限扩大,那你就任由他无限扩大吗?不是的,为了不影响效率,如果链表的长度超过了8,进变形为红黑树
5.红黑树可以变为链表吗?可以的,当红黑树长度低于6的时候就转型链表

8.3阿里面试问题:

【Java面试集合】hashmap底层原理_第3张图片

你可能感兴趣的:(【Java面试集合】hashmap底层原理)