最近几天一直在面试,对于面试公司,我也有了一些了解,对于公司的问题也做了总结。基本现在所有的互联网公司都在大数据边缘,所以对于集合相当重视。那么我下面以问答的形式把这几天的问题和答案总结下,希望对你们有所帮助。
简单谈谈HashMap和HashTable的区别?
第一点:发布时间不一样
时间上HashTable是jdk1.0出来的,而HashMap则是jdk1.2出来的。HashMap是继承自AbstractMap类,HashTable是继承Dictionary类,二者都实现了Map接口。现在这个Dictionary类已经过时,所有继承自它的HashTable也已经过时(见其源码中的注释),HashMap是目前比较流行的也是最广泛的数据类型。
第二点:线程是否安全
HashMap不是线程安全的。
HashTable是线程安全的。Hashtable 线程安全很好理解,因为它每个方法中都加入了Synchronize。
第三点:key和value是否允许null值
它们两者都不会在编译阶段出问题,运行阶段HashMap是可以运行通过,但是我们的HashTable是会报NullPointerException异常。
HashMap是允许有key和value是null。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。验证代码如下
第四点:它们计算hash值方式:hash值是用来计算存储下标的
因为不明白,所以看了底层代码怎么实现hash值的计算。
Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模,如下
HashMap的hash值计算方式如下:
第五点:内部实现使用的数组初始化和扩容方式不同
HashTable的初始容量是11,HashTable的初始容量是16.两者的填充因子默认都是0.75。
HashMap扩容时是:当前容量X2。在扩大容量时须要重新计算hash
Hashtable扩容时是:当前容量X2+1。
第六点:遍历方式不同
HashMap遍历使用的是Iterator迭代器;
HashTable遍历使用的是Enumeration列举;
什么是线程安全,什么是非线程安全?
非线程安全是指多线程操作同一个对象可能会出现问题。而线程安全则是多线程操作同一个对象不会有问题。
线程安全必须要使用很多synchronized关键字来同步控制,所以必然会导致性能的降低。
HashMap的底层实现原理
首先有一个每个元素都是链表(可能表述不准确)的数组,当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树,这样大大提高了查找的效率。当链表数组的容量超过初始容量的0.75时,再散列将链表数组扩大2倍,把原链表数组的搬移到新的数组中
如何创建一个线程安全的Map?
第一种方式
Map<String, String> hashtable = new Hashtable<>();
第二种方式
Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());
第三种方式
Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();
为什么HashMap是非线程安全的?
HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。因为它每个方法中都没有加入了Synchronize关键字,是非同步的。