Q:java的集合框架之 List
ArrayList:
底层实现为动态数组,初始容量为10,插入元素时会动态扩容。常数随机访问时间。非同步(非线程安全的)。
ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类
动态扩容策略:1.5倍+1
扩容时,底层采用System.arraycopy()来实现,实际上调用了native的c方法:memmove(),效率较高。
fail-fast机制:通过modCount来记录结构上的修改,防止并发修改。迭代器会检查。
允许其中存在null元素。
Q:LinkedList源码?
实现了List、Deque接口,底层实现是双向(循环)链表,head节点不包含数据,可被当作堆栈、队列或者双端队列使用。
双端队列的插入,addBefore(e, entry)的实现。
构造新的Entry,前后指针分别指向 entry 的pre和entry,然后调整entry的next以及entry的previous。
ListIterator接口。
可以被当作堆栈、队列或者双端队列使用,是因为实现了Deque接口:Deque除了提供队列或者双端队列对应的接口之外,还提供了stack接口(push,pop,peek)
与队列对应的接口(offer,poll,peek)
Q:HashSet源码?
底层实现是用HashMap实现的,value为一固定的Object。不保证元素顺序恒久不变。
Q:HashMap源码?
底层实现:“链表散列”的数据结构,数组链表,数组和链表的结合体:一个数组,数组中每个元素又是一个链表。
主要是对key的操作,通过key确定在数组中的位置:
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
这是hash的实现方式,面试竟然被问到过这个怎么实现的。
然后定位index时,通过
static int indexFor(int h, int length) {
return h & (length-1);
}
来实现。这里length保证是2的幂次方,这样使得计算出来的index是比较均匀分布的,不同的hash值一般位置不同。另外hash函数也保证了元素分布比较均匀。
归纳起来简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。
loadFactor:0.75,装填因子。初始容量为16.
当HashMap中元素的个数超过数组大小*loadFactor时,便会出发resize(rehash)。resize会扩容,容量扩展为2倍。
Q:fail-fast和fail-safe
fail-fast 是说,在通过迭代器遍历集合的过程中,如果出现集合结构上的变化,会抛出ConcurrentModificationException()异常。是通过expectedModCount与 modCount比较得到的。迭代器的快速失败行为不能得到保证,只是尽最大努力。
到底什么意思,就是说只有当访问next或者hasNext方法时,出发到了expectedModCount与 modCount的检查,才有肯能会抛出该类型的异常。
fail-safe是说,任何对集合结构上的修改都会在一个复制的结构上进行修改,因此不抛并发修改异常。
fail-fast常见的: HashMap,Vector,ArrayList,HashSet。
fail-safe常见的:CopyOnWriteArrayList,ConcurrentHashMap
后者有内存开销。