关注wo们
老哥们,接上篇《Java开发岗面试题--基础篇(一) 》, 本期推出Java开发岗面试题--基础篇(二),来看看Java中的集合、多线程、异常体系等知识在面试中是怎么体现的。HashMap和HashTable的区别?
HashMap和HashTable是Map接口的实现类,它们大体有以下几个区别:
继承的父类不同。HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。
线程安全性不同。HashTable中的方法是Synchronize修饰的,而HashMap中的方法在缺省情况下是非Synchronize的。因此,HashTable是线程安全的,HashMap是非线程安全的。
key和value是否允许null值。HashTable中,key和value都不允许出现null值。但是如果在HashTable中有类似put(null,null)的操作,编译同样可以通过,因为key和value都是Object类型,不过运行时会抛出NullPointerException异常,这是JDK的规范规定的。HashMap中,null可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是HashMap中没有该键,也可能是该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。
Map集合有哪些实现类?
分别具有什么特征?
实现类 |
特征 |
HashMap |
线程不安全的键值对集合。允许null值,key(最多只允许一个)和value都可以 |
HashTable |
线程安全的键值对集合。不允许null值,key和value都不可以 |
TreeMap |
能够把它保存的记录根据键排序的集合。默认是按升序排序 |
如何解决HashMap线程不安全问题?
Collections.synchronizedMap() 方法。
Java.util.concurrent.ConcurrentHashMap类。ConcurrentHashMap类的方法内部使用了synchronized保证线程安全。
HashMap的底层实现原理?
在JDK1.6、JDK1.7中,HashMap采用位桶+链表实现,即使用链表处理hash冲突,同一hash值的键值对会被放在同一个位桶里,当桶中元素较多时,通过key值查找的效率较低。而在JDK1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值8时,将链表转换为红黑树,这样大大减少了查找时间。当创建HashMap时会先创建一个数组,调用put()方法存数据时,先根据key的hashcode值计算出hash值,然后用这个哈希值确定在数组中存放的位置,再把value值放进去,如果这个位置本来没放东西,就会直接放进去,如果之前就有,就会生成一个链表,把新放入的值放在头部,当用get方法取值时,会先根据key的hashcode值计算出hash值,确定位置,再根据equals方法从该位置上的链表中取出该value值。Hash碰撞怎么产生,怎么解决?
对象进行hash运算的前提是实现equals()和hashCode()两个方法,那么hashCode()的作用就是保证对象返回唯一hash值,但当两个对象计算值一样时,这就发生了碰撞冲突。下面将介绍如何处理冲突,当然其前提是一致性hash。解决hash碰撞有以下几种方法:开放地址法
再哈希法
链地址法(拉链法)
将所有关键字为同义词的记录存储在同一线性链表中。如下:
![e22446fe5ebafbe0bc22efedc527af79.png](https://i-blog.csdnimg.cn/blog_migrate/e0a46ef30a53ba71a98205ad705c0726.png)
HashMap为什么需要扩容?
当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为16*2=32,即扩大一倍。然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。比如说,我们有1000个元素就是new HashMap(1000),但是理论上来讲new