一、请你说说HashMap和Hashtable的区别
HashMap和Hashtable多是典型的Map实习,他们的区别在于是否线程安全,是否可以存入null值。
1.Hashtable在实现Map接口时保证了线程安全性,而HashMap则是非线程安全的。所以,Hashtable的性能不如HashMap,因为为了保证线程安全它牺牲了一些性能。
2.Hashtable不允许存入null,无论是以null作为key或value,都会引发异常。而HashMap是允许存入null的,无论是以null作为key或value,都是可以的。
加分回答 虽然Hashtable是线程安全的,但仍然不建议在多线程在多线程环境下使用Hashtable。因为它是一个古老的API,从Java 1.0 开始就出现了,它的同步方案还不成熟,性能不好。如果要在多线程环境下使用HashMap,建议使用ConcurrentHashMap。它不担保证了线程安全,也通过降低锁的粒度提高了并发访问时的性能。
二、请你说说HashMap底层原理
数据结构 在JDK8中,HashMap底层是采用“数组+链表+红黑树”来实现的。HashMap是基于哈希算法来确定元素的位置(槽)的,当我们集合中存入数据时,它会计算传入的Key的哈希值,并利用哈希值取余来确定槽的位置。如果元素发生碰撞,也就是这个槽已经存在其他的元素了,则HashMap会通过链表将这些元素组织起来。如果碰撞进一步加剧,某个链表的长度达到了8,则HashMap会创建红黑树来代替这个链表,从而提高对这个槽中数据的查找的速度。HashMap中,数组的默认初始化容量为16,这个容量会以2的指数进行扩容。具体来说,当数组中的元素达到一定比例的时候HashMap就会扩容,这个比例叫做负载因子,默认为0.75。自动扩容机制,是为了保证HashMap初始化时不必占据太大的内存,而在使用期间又可以实时保证有足够大的空间。采用2的指数进行扩容,是为了利用位运算,提高扩容运算的效率。put()流程 put()方法的执行过程中,主要包含四个步骤:1.判断数组,若发现数组为空,则进行首次扩容。2.判断头节点,若发现头节点为空,则新建链表节点,存入数组。3.判断头节点,若发现头节点非空,则将元素插入槽内。4.插入元素后,判断元素的个数,若发现超过阈值则再次扩容。其中,第3步又可以细分为如下三个步骤:1.若元素的key与头节点一致,则直接覆盖头节点。2.若元素为树型节点,则将元素追加到链表中。追加后,需要判断链表长度以决定是否转为红黑树。如链表长度达到8、数组容量未达到64,则扩容。若链表长度达到8、数组容量达到64,则转为红黑树。扩容机制 向HashMap中添加数据时,有三个条件会触发它的扩容行为:1.如果数组为空,则进行首次扩容。2.将元素接入链表后,如果链表长度达到8,并且数组长度小于64,则扩容。3.添加后,如果数组中元素超过阈值,即比例超出限制(默认为0.75),则扩容。并且,每次扩容时都是将容量翻倍,即创建一个2倍大的新数组里。由于HashMap中数组的容量为2^N,所以可以用位移运算计算新容量,效率很高。
加分回答 HashMap是非线程安全的,在多线程环境下,多个线程同时触发HashMap的改变时,有可能会发生冲突。所以,在多线程环境下不建议使用HashMap,可以考虑使用Collections将HashMap转为线程安全的HashMap,更为推荐的方式则是使用ConcurrentHashMap。
三、你知道哪些线程安全的集合?
java.util包下的集合类中,大部分都是非线程安全的,但也有少数的线程安全的集合类,例如Vector、Hashtable,他们都是非常古老的API。虽然它们是线程安全的,但是性能很差,已经不推荐使用了。对于这个包下非线程安全的集合,可以利用Collections工具类,该工具类提供的synchronizedXxx()方法,可以将这些集合包装成线程安全的集合类。从JDK1.5开始,并发包下新增了大量高效的并发的容器,这些容器按照实现机制可以分为三类。第一类是以降低锁粒度来提高并发性能的容器,它们的类名以Concurrent开头,如CurrentHashMap。第二类是采用写时复制技术实现的并发容器,它们的类名以CopyOnWrite开头,如CoptOnWriteArrayList。第三类是采用Lock实现的阻塞队列,内部创建两个Condition分别用于生产者和消费者的等待,这些类都实现了BlockingQueue接口,如ArrayBlockingQueue。
加分回答 Collections还提供了如下三类方法来返回一个不可变的集合,这三类方法的参数是原有的集合对象,返回值是该集合的“只读”版本。通过Collections提供的三类方法,可以生成“只读”的Collection或Map。emptyXxx():返回一个只包含指定对象的不可变得集合对象 unmodifiableXxx():返回指定集合对象得不可变视图。