Java面试题——第四篇(集合)

1. 怎样确保一个集合不能被修改

使用 Collections.unmodifiableCollection(Collection c) 方法来创建一个只读集合。

2. Comparable和Comparator区别

  • Comparable是让集合元素自身具备比较性,即元素所属的类需要实现Comparable接口,并覆盖compareTo(T o)方法,这意味着比较的逻辑是嵌入在元素类中的。
  • Comparator:是让集合具备比较性,这种方式允许在不修改元素类的情况下,为集合提供自定义的比较逻辑

3. ArrayList默认大小是多少?如何进行扩容?

在用无参构造来创建对象时创建一个空数组,长度为0.这个时候先分配一个 默认大小为10,在进行扩容。
有参数构造中,传入的参数是正整数就按照传入的参数来确定创建数组的大小,在进行扩容。一般扩容到原来的1.5倍

4. 如何解决并发下ArrayList不安全的问题

  1. 使用Vector。
  2. 使用Collections工具类:Collections.synchronizedList(new ArrayList<>());
  3. 使用CopyOnWriteArrayList。

5. CopyOnWriteArrayList是什么?可以用于什么场景?有哪些缺点?

CopyOnWriteArrayList(免锁容器)是一个并发容器。适合读多写少的场景。在CopyOnWriteArrayList中,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全的执行
缺点:

  1. 由于写操作的时候,需要拷贝整个数组,消耗内存,如果原数组内容过大,可能会导致young gc或者full gc。
  2. 不能用于实时读的场景,像拷贝数组,新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList能做到最中意执行,但是无法满足实时性要求。

CopyOnWriteArrayList 透露的重要思想

  • 读写分离
  • 最终一致性
  • 使用另外开辟空间的思路,来解决并发冲突。

6. HashMap实现原理

HashMap中put元素时,首先根据key的hashcode计算hash值,根据hash值得到这个元素在数组中的位置,如果数组在该位置上已经存在元素,则将元素以链表的方式存放,如果链表长度大于8,则判断数组长度是否大于16,若大于则转换成红黑树,否则,扩容数组长度。

7. HashMap的key是自定义的类吗

可以,但是 HashMap的key如果是自定义的类,就需要重写hashcode()和equals()方法

8. 为什么String、Integer这样的包装类适合作为HashMap的key

String、Integer等包装类特性能够保证Hash值的不可更改性和计算准确性,能够有效减少Hash碰撞的几率。

  • 都是final类型,即不可变性,保证key的不可更改性,不会存在获取Hash值不同的情况。
  • 内部已经重写了equals()、hashcode()方法,遵守了HashMap内部的规范,不容易出现Hash值计算错误的情况。

9. ConcurrentHashMap和HashTable的区别

  1. 底层数据结构:JDK1.7的ConcurrentHashMap底层采用分段数组+链表,JDK1.8采用的数据结构跟HashMap一样,数组+链表/红黑树。
  2. 实现线程安全的方式
  • 在JDK1.7的时候,ConcurrentHashMap采用分段锁对整个数组进行了分割,每个锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在竞争问题,提高了并发效率。JDK1.8摒弃了Segment概念,而是直接用Node数组+链表+红黑树的结构来实现,并发控制使用synchronized和CAS。(根据key计算出hash值,如果hash无冲突,则cas放入数组的节点中,如果在数组中hash值已经存在,锁定数组的头节点,添加到链表中 ) 整个过程看起来就像是优化过且线程安全的HashMap。
  • HashTable,使用synchronized来保证安全。效率非常低下,当一个线程访问同步方法时,另一个线程也访问同步方法,可能会进入阻塞或者轮询状态。如果一个线程调用put操作,则另一个线程无法调用put以及get操作。

10. 为什么ConcurrentHashMap和HashTable不支持key,value为null

因为HashMap是非线程安全的,默认单线程环境中使用,如果get(key)为null。可以通过containsKey(key)方法来判断这个key的value为null,还是不存在这个key。而ConcurrentHashMap、HashTable是线程安全的,在多线程操作时,因为get(key)和contains(key)两个操作和在一起不是一个原子性操作,可能在contains(key)时发现存在这个键值对,但是get(key)时,其他线程删除了键值对,导致get(key)返回的是null,所以无法区分value为null还是不存在这个key(核心:并发环境下,containsKey返回key存在,但是其他线程删除这个key,get的时候为空。所以无法判断出这个key是否存在,从而导致异常。)

11. 快速失败(fail-fast)和安全失败(fail-safe)区别

Iterator安全失败是基于对底层集合做拷贝,因此,不受源集合上修改的影响。java.util包下面所有的集合类都是快速失败的,而java.util.concurrent包下所有类都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败不会抛出这种异常。

12. ConcurrentModificationException是什么?

并发修改异常,一些对象不允许并发修改,当这些修改行为被检测到的时候,就会抛出这个异常。
一些集合的Iterator实现中,如果提供这种并发修改异常检测,那么这些Iterator可以称为【fail-fast Iterator】。即检测到并发修改时,直接抛出异常,而不是继续执行,等到获取到一些错误值时在抛出异常。

异常检测主要是通过modCount和expectedModCount两个变量实现。

  • modCount集合被修改的次数,一般是被集合持有,每次调用add、remove方法会导致modCount+1.
  • expectedModCount,一般是被Iterator持有,一般在Iterator初始化时赋初始值,在调用Iterator的remove()方法时更新。

总结:
ArrayList和HashMap的Iterator都是fail-fast,Iterator在获取下一个元素,删除元素时,都会比较modCount和expectedModCount,不一致就会抛出异常。所以,当使用Iterator遍历元素时,需要删除元素,一定要使用Iterator的remove()方法来删除。而不是直接调用ArrayList或者HashMap的remove方法。否则会导致Iterator中的expectedModCount没有被及时修改,之后获取下一个元素或者删除元素时,modCount和expectedModCount不一致,然后抛出ConcurrentModificationException

13. 什么是阻塞队列,阻塞队列的实现原理是什么

阻塞队列是一个支持两个附加操作的队列。

两个附加操作分别是:

  • 在队列为空时,获取元素的线程会等待队列变为非空。
  • 队列满时,存储元素的线程会等待元素可用。
    JDK提供了7个阻塞队列,分别为:
  • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列
  • LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列
  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列
  • DelayQueue: 一个使用优先级队列实现的无界阻塞队列
  • SynchronousQueue: 一个不存储元素的阻塞队列
    。。。。。

Java5 之前实现同步存取时,可以使用普通的一个集合,然后在使用线程的协作和同步实现生产者、消费者模式。主要技术是用好wait、notify、synchronized这些。在Java5之后,可以使用阻塞队列实现。

14. 什么是SynchronousQueue

SynchronousQueue是一个没有容量、无缓冲等待队列,他不存储元素,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

just-julie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值