collection(list,set,map)HashMap

Posted lgg20

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了collection(list,set,map)HashMap相关的知识,希望对你有一定的参考价值。

collection里面有什么子类?(list和set是实现了collection接口的。) 

List:

1.可以允许重复的对象(可重复,有序集合)。
2.可以插入多个null元素。
3.常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
————————————————

Set:

1.不允许重复对象(不可重复,无序集合)。
2 只允许一个 null 元素
3.Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序集合。而且可以重复。
————————————————
Map:

1.Map不是collection的子接口或者实现类。Map是一个接口。
2.不允许重复元素。
3. Map 里你可以拥有随意个 null 值但只能有一个 null (key)键。
4. Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)
————————————————

List 的实现类有 ArrayList,Vector 和 LinkedList:

ArrayList 和 Vector 内部是数组结构,线程不安全,在查询效率上会高很多。 Vector 是线程安全的,性能会稍慢一些。

LinkedList:是双向链表的数据结构,在做查询时会按照序号索引数据进行前向或后向遍历,查询效率偏低,插入速度较快。

Set 的实现类有 HashSet 和 TreeSet:

HashSet:内部是由哈希表(实际上是一个 HashMap 实例)支持的。集合元素可以是null,但只能放入一个null。无序的。

TreeSet:是二叉树实现的,有序的,或者根据创建 set 时提供的 Comparator 进行排序。

LinkedHashSet:是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。有序的,查询快,插入慢。当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。

Map 的实现类有 HashMap,Hashtable、TreeMap和 LinkedHashMap:

Hashtable:内部存储的键值对是无序的是按照哈希算法进行排序,与 HashMap 最大的区别就是线程安全。键或者值不能为 null,为 null 就会抛出空指针异常。

HashMap: 继承Map接口,实现了哈希表,允许null,线程不安全。哈希表结构其实就是数组+链表;在JDK1.8中规定:当链表长度大于8时,链表长度就转换为红黑树,大大                        提高了查找效率。

TreeMap:基于红黑树 (二叉树) 数据结构实现,按 key 排序,默认的排序方式是升序。

LinkedHashMap:有序的 Map 集合实现类,通过插入排序,相当于一个栈,先 put 进去的最后出来,先进后出。

————————————————-------------------------------------------------------------------------------------------------------------------

Hashmap的数据结构是什么样子的?自己如何实现一个hashmap?

主要数据结构即为数组+链表。默认长度是16。

Hashmap的底层数据结构是由数组+链表组成的,是线程不安全,允许key和value为null。底层结构数组叫哈希桶,而桶内则是链表,链表中的节点Node存放着实际的元素。

HashMap的底层原理是什么?

基于hashing的原理,jdk8后采用数组+链表+红黑树的数据结构。我们通过put和get存储和获取对象。当我们给put()方法传递键和值时,先对键做一个hashCode()的计算来得到它在bucket数组中的位置来存储Entry对象。当获取对象时,通过get获取到bucket的位置,再通过键对象的equals()方法找到正确的键值对,然后在返回值对象。

HashMap底层是如何实现的?

HashMap先得到key的散列值,在通过扰动函数(减少碰撞次数)得到Hash值,接着通过hash & (n -1 ),n位table的长度,运算后得到数组的索引值。如果当前节点存在元素,则通过比较hash值和key值是否相等,相等则替换,不相等则通过拉链法查找元素,直到找到相等或者下个节点为null时。

什么是hash(哈希)?

hash的定义:Hash一般翻译为散列,哈希是指一个过程,这个过程就是把任意长度的输入,通过哈希算 法,变换成固定长度的输出,所输出的称为哈希值。

Hash的计算规则(算法)?

第一步:调用key.hashCode()。

第二步:用hashCode值高16位异或低16位(右移16位然后与原先的hashCode值异或,即自己的高半区与低半区做异或,这样混合后的低位掺杂了高位的部分信息):

    return (key==null)?0:h=key.hashCode()^h>>>16;

第三步:取模运算,取模运算:h&(length-1) 其中length为table数组长度。h&(length-1) 等价于h%length。

技术图片

Hashmap的哈希怎么求出下标位置?

1、进行哈希散列 hash();
int h=key.hashCode();
2、这里得到的是二进制数:比如:
h=key.hashCode(); //得到的是32位二进制数
** h:0000 0000 0000 1101 0001 0101 0101 1111**
h>>>16:0000 0000 0000 0000 0000 0000 0000 1101

3、//右移16位
hash=h^(h>>>16):0000 0000 0000 1101 0001 0101 0101 0010
4、转换成二进制后 >>>右移16位和hahsCode值进行位异或.

5、然后index=(16-1)&&hash;index: 0000 0000 0000 0000 0000 0000 0000 0010
转换成二进制后index=2就是存储在数组中的下标位置。

HashMap的长度为什么是2的倍数?

在HashMap的操作流程中,首先会对key进行hash算法得到一个索引值,这个索引值就是对应哈希桶数组的索引。为了得到这个索引值必须对扰动后的数跟数组长度进行取余运算。即 hash % n (n为hashmap的长度),又因为&比%运算快。n如果为2的倍数,就可以将%转换为&,结果就是 hash & (n-1)。所以这就解释了为什么HashMap长度是2的倍数。

哈希碰撞是什么,怎么解决?

两个不同的原始值在经过哈希运算后得到同样的结果,这样就是哈希碰撞。
解决方法:开放定址法,链地址法。

开放定址法:   原理是在HashMap中同样哈希值的位置以一串链表存储起来数据,把多个原始值不同而哈希结果相同的数据以链表存储起来。
链地址法:   当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。
ConCurrentHashMap与HashTable区别?

两者都是线程安全的,但是hashTable锁住的是整个map,效率低下。而ConcurrentHashMap使用的是cas+synchronized机制,不会锁定整个map,而是锁定table数组位置对应的链表。
一般不要使用hashTable,推荐使用ConCurrentHashMap。

谈一下hashMap中什么时候需要进行扩容,扩容resize()又是如何实现的?

调用场景:

1.初始化数组table

2.当数组table的size达到阙值时即++size > load factor * capacity 时,也是在putVal函数中

实现过程:(细讲)

1.通过判断旧数组的容量是否大于0来判断数组是否初始化过

否:进行初始化

  • 判断是否调用无参构造器,
    • 是:使用默认的大小和阙值
    • 否:使用构造函数中初始化的容量,当然这个容量是经过tableSizefor计算后的2的次幂数

是,进行扩容,扩容成两倍(小于最大值的情况下),之后在进行将元素重新进行与运算复制到新的散列表中

概括的讲:扩容需要重新分配一个新数组,新数组是老数组的2倍长,然后遍历整个老结构,把所有的元素挨个重新hash分配到新结构中去。

PS:可见底层数据结构用到了数组,到最后会因为容量问题都需要进行扩容操作

谈一下hashMap中get是如何实现的?

对key的hashCode进行hashing,与运算计算下标获取bucket位置,如果在桶的首位上就可以找到就直接返回,否则在树中找或者链表中遍历找,如果有hash冲突,则利用equals方法去遍历链表查找节点。

谈一下HashMap中hash函数是怎么实现的?还有哪些hash函数的实现方式?

对key的hashCode做hash操作,与高16位做异或运算

还有平方取中法,除留余数法,伪随机数法

为什么不直接将key作为哈希值而是与高16位做异或运算?

因为数组位置的确定用的是与运算,仅仅最后四位有效,设计者将key的哈希值与高16为做异或运算使得在做&运算确定数组的插入位置时,此时的低位实际是高位与低位的结合,增加了随机性,减少了哈希碰撞的次数。

为什么是16?为什么必须是2的幂?如果输入值不是2的幂比如10会怎么样?

https://blog.csdn.net/sidihuo/article/details/78489820

https://blog.csdn.net/eaphyy/article/details/84386313

1.为了数据的均匀分布,减少哈希碰撞。因为确定数组位置是用的位运算,若数据不是2的次幂则会增加哈希碰撞的次数和浪费数组空间。(PS:其实若不考虑效率,求余也可以就不用位运算了也不用长度必需为2的幂次)

2.输入数据若不是2的幂,HashMap通过一通位移运算和或运算得到的肯定是2的幂次数,并且是离那个数最近的数字

谈一下当两个对象的hashCode相等时会怎么样?

会产生哈希碰撞,若key值相同则替换旧值,不然链接到链表后面,链表长度超过阙值8就转为红黑树存储

如果两个键的hashcode相同,你如何获取值对象?

HashCode相同,通过equals比较内容获取值对象

以上是关于collection(list,set,map)HashMap的主要内容,如果未能解决你的问题,请参考以下文章

Collections,Collection ,Map,List,Set的区别?

List接口Set接口和Map接口

collection(list,set,map)HashMap

JAVA Collection接口中List Map 和Set的区别(转)

Java Collection集合中List,Set,Queue以及Map的使用

关于set,list,map的理解