面试题(一)
1、LinkedList和ArrayList的区别
LinkedList的底层实现是一个双向链表,ArrayList的底层实现是一个数组,ArrayList查找和修改速度比较快,因为它在内存中是一片连续的空间,这意味着可以根据数组的首地址和位偏移量直接计算出想要访问的元素在内存中的位置,而链表在内存中不是一段连续的空间,所以查找时需要从首元素开始依次获得下一个元素地址。而链表增加和删除操作更快,因为这两种操作对于数组来说需要移动数据。
ArrayList的空间浪费主要体现在在list的结尾要预留一定的容量空间,而LinkedList的空间花费主要体现在它的每个元素都需要消耗空间来存放地址。
ArrayList的扩容:通过无参方法构造时,默认初始容量为0,增加一个元素的时候扩容为10,当后续还需要扩容时,每次扩容为之前的1.5倍
2、HashMap
HashMap本质上存储结构是一个数组,辅以链表和红黑树,数组结构在查询和插入删除复杂度方面分别为O(1)和O(n)。链表结构在查询和插入删除复杂度方面分别为O(n)和O(1)。
HashMap的初始容量为16,存储的时候通过哈希算法计算出key对应的hash值,这里是进行了二次hash,然后对数组长度进行取模,然后在数组对应下标位置,添加一个Entry类,这是HashMap的一个静态内部类,主要有hash,key,value,next四个属性,当存入的键值对发生hash冲突时,将next指向最新插入的Entry对象,这里使用的是链式数据结构,当同一个下标下链表长度超过阈值(8)时,将链表转化为红黑树。来减少查找的时间,当数组容量超过初始容量的0.75时,开始扩容至原来的两倍
HashMap支持Null键,但总是存储在数组的第一个节点上
3、CourrentHashMap
因为HashMap不是线程安全的,所以多线程环境下,使用hashmap操作会引起死循环,而HashTable效率比较低。
CourrentHashMap主要采用锁分段技术,什么是锁分段技术呢,例如多线程访问HashTable时必须要竞争同一把锁,如果将容器里的数据分组,每一组用不同的锁,这样在访问时当其中一组被访问时,其他组的数据也能被其他线程访问。当出现需要跨组的操作时,要按照顺序锁定所有组,在操作结束后按顺序释放锁。
CourrentHashMap是由Segment数组结构和HashEntry数组结构组成,Segment数组结构和HashEntry数组结构组成,Segment是一种可重入锁ReentrantLock,在CourrentHashMap里扮演锁的角色,HashEntry用于存储键值对数据,一个CourrentHashMap里包含一个Segment数组,Segment的结构和HashMap结构类似,是一种数组加链表的结构,一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改,必须先获得它对应的Segment锁
4、队列
队列通常有两种实现方式一种是循环数组,另一种是链表,
特性:先进先出
双端队列支持在队头和队尾同时增加或删除元素
优先级队列,元素可以按照任意顺序插入,但是检索的时候会按照排序的顺序检索,里面使用了堆的数据结构,是一个可以自我调整的二叉树,对树进行添加和删除操作,可以让最小的元素移动到根,而不必花费时间对元素进行排序。
队列的应用:
对于许多线程问题,可以使用队列来将其形式化,生产者线程向队列插入元素,,消费者线程则取出他们,使用队列,可以安全的从一个线程向另一个线程传递数据。当试图向队列添加元素而队列已满,或者是想从队列移出元素而队列为空的时候,阻塞队列导致线程阻塞。在协调多个线程之间的合作时,阻塞队列是一个有用的工具,工作者线程可以周期性的将中间结果存储在阻塞队列中,其他的工作者线程移出中间结果并进一步加以修改。