今天学习了一下HashMap所以来记录一下笔记
HashMap的工作原理
HashMap的底层是由数组与单向链表实现的,数组中每个元素都是链表,由node内部类实现,还有get与put方法
存储数据时,也就是调用put方法时,数据(k\v),会先通过hash(k)来计算他的hash值,结合数组长度来找到数组下标,然后就是调整数组长度,如果数组长度到达的阈值就会自动扩容为2n,再然后就是看这个hash值在HashMap中是否存在,若不存在则直接插入,若存在则发生碰撞,如果该hashmap存在了并且equals返回true则更改其键值对,如果返回false插入,Java7是前插法,Java8是尾插法,或者在红黑树中
获取数据时,也就是get方法,用hash(k)获取到hash值,然后结合长度获得数组下标,然后利用数组下标,按顺序遍历,利用equals方法比较获取v值
HashMap在存储数据时的流程?
HashMap在存储(k,v)时,会先利用hash(k)来计算出hash值然后利用数组长度来得出数组下标,然后这个hash值在HashMap是否存在,若不存在则直接插入,若存在则会发生碰撞,但是若equals返回true的话,就会改变其键值对,若equals返回false,则在尾部插入或者在树按顺序插入。
为什么HashMap的数组长度为2的幂次方?
HashMap为了高效存储,减少碰撞,所以将数据均匀分配,我们知道HashMap的存储范围前后加起来只有40多亿,但是如果现在有一个长度为40多亿的数组储存在HashMap中,则放不开了,所以HashMap就是将数组的长度取模后得到的余数来作为存储数据的位置也就是数组下标,但是HashMap的计算方式时hash&(n-1),只有n为2的幂次方时,hash&(n-1)才等价于hash%n,所以这也就是说明了为什么HashMap的数组长度为2的幂次方。
HashMap多线程容易造成死锁问题
这是因为在jdk1.7及之前HashMap的扩容机制都是在采用头插法这也就导致了,在多线程操作时,一个桶有多个元素进行扩容时,多个线程对一个链表进行操作,头插法的数据指向容易出错,从而出现环形链表,使得查询操作陷入死循环从而无法操作
所以在jdk1.8开始就使用了尾插法,这样当数据需要扩容时,直接在尾部插入即可从而避免了形成环形结构,但是还是建议在多线程下,尽量避免使用HashMap
HashMap与HashTable的区别
HashMap是线程不安全的,HashTable是线程安全的
正是因为HashTable的线程是安全的所以比HashMap的效率低
HashMap最多只允许一个键的内容为null,多个值为null而HashTable不允许
HashMap与HashTable到达扩容条件时HashMap为2n,而HashTable为2n+1;
HashMap需要重新计算hash值,HashTable直接用HashCode
HashMap ,LinkedHashMap与TreeMap的区别
HashMap的线程不安全的,最多只允许一个键的内容为null,多个值内容为null,当到达扩容机制时为2n
LinkedHashMap是有顺序的当遍历时,先遍历出的肯定是先存储的,然后就是遍历的比较慢
TreeMap使用SortMap接口的,能用他的键进行排列。
介绍一下LinkedHashMap
LinkedHashMap是HashMap的子类,底层使用双向链表来维护元素的插入顺序,当他遍历的时候就为该顺序,此顺序可以为元素的插入顺序也能为最近最少使用顺序(LRU),put,get,remove都为使用顺序,这样的话就能实现淘汰机制与内存管理,释放那些不经常访问的元素
来说一下你对List的理解
List它分为ArrayList,LinkedList和Vector
先来说一下我对ArrayList的理解吧,它的底层是用数组实现的,当你创建一个new ArrayList的时候他的大小是0,当你使用添加元素的时候就变为10,因为它的底层是数组吗,所以他的优点是也就是,方便随机查找,缺点也很明显就是不便于插入和删除,需要向后挪动元素,然后就是当他的容量快要满的时候怎么办,这就涉及一个扩容的问题,一般是扩容到他的1.5倍,这是创立一个新数组,然后将原数组中的内容移过来,然后再添加数据,由此可见这是非常麻烦的,所以我们要尽量避免让他扩容,我们可以提前预见添加数据的长度,然后对Arraylist来提前设定,我们也可以提前调用ensureCapacity
方法来设定
然后就是我对Linkedlist的理解,他其实是一个双向链表来实现的,所以在他插入和删除就便利一点,然后他也不需要再向后挪动元素,只需要改变其指针指向就可以,他的线程是不安全的
Vector不推荐使用,因为他也是基于数组实现的,与Array List基本类似,所以不推荐使用,然后就是他的线程是安全的
List,Set,Map的区别
List是排序的一把好手,她存储的数据是有顺序的和可重复的
Set的存储的数据是不可重复的
Map的存储的数据是键值对形式的,(key,value),就像是数学中的函数f(x) = y,每一个键key都有唯一的值来对应
ArrayList和Array之间的区别
ArrayList底部是一个动态数组,Array的底部是一个静态数组
1.ArrayList它可以根据需要来进行缩容和扩容,Array他不一样数组确定了就是确定了
2.ArrayList它可以使用泛型来确保类型安全,而Array不能
3.ArrayList她存放的是对象,所以要转换为对象才能存储,而Array则不用
4.ArrayList支持多种API的方法,而Array没有
5.ArrayList创建时不能设定大小,而Array必须设定
ArrayList与LinkedList的区别
我们可以从线程是否安全,底层实现,存储数据,是否支持随机访问与占用内存来观察区别
他们两个线程都不是同步的,所以线程都不安全
ArrayList底层是一个数组,而LinkedList是一个双向链表
ArrayList存储数据的时候是会被已有数据影响的,所以在他再中间插入元素是会向后移动元素,而LinkedList只需要改变指针指向就可以
ArrayList是支持随机访问的,而LinkedeList则 不支持
占用内存的角度来说,ArrayList占用的话就是他的剩余容量,LinkedList来说就是每个元素之间的双向指针
HashSet,LinkedHashSet和TreeSet的异同点
他们都是基于set接口的实现类都保证了元素的唯一性,都是线程不安全的
从底层实现来讲,HashSet底层是哈希表,LinkedhashSet
底层是哈希表+链表,TreeSet是红黑树
然后就是应用场景问题,HashSet不能保证存储顺序的问题,而LinkedHashSet它可以保证存储与拿出顺序,而TreeSet可以保证存储内部按顺序存放