1、ArrayList的扩容机制是什么?
ArrayList在创建的时候,如果不指定初始容量,默认容量minCapacity为0,在add方法中,首先调用ensureInternalCapacity(如果minCapacity小于10,设置为10),然后调用ensureExplicitCapacity(如果现需容量minCapacity大于elementData容量,则扩容),grow的逻辑很简单。首先找出当前容量,把新容量设置为旧容量的1.5倍(通过位运算左移实现),如果扩容后容量newCapacity比minCapacity还小,则设置newCapacity=minCapacity,如果newCapacity比极限容量(Integer.MAX_VALUE-8)要大,则设置新容量=Integer.MAX_VALUE,接着使用该新容量初始化一个新的数组,使用Arrays.copyOf(elementData, newCapacity)将原有elementData中的元素等位复制为一个新数组。
ArrayList使用的优化:
在new ArrayList时指定初始化容量,以减少扩容次数
在add大量元素之前手动调用ensureCapacity方法,以减少增量重新分配次数;
2、ArrayList中基础数据域Object[] elementData使用transient修饰的,那它是怎么序列化的?为什么?
ArrayList在被序列化为文件、网络io等时,会调用被序列化类中的writeObject方法,找不到writeObject则调用工具类中defaultWriteFields方法,在ArrayList中writeObject私有的,序列化时使用反射调用。之所以不使用类中属性elementData直接来序列化,主要原因是elementData是一个缓存数组,为了性能考虑,它通常会预留一些容量,因此可能有大量空间没有实际存储元素。不直接序列化elementData可以保证序列化实际有值的那些元素,而不是序列化整个数组。
2、LinkedList和ArrayList有什么区别?
LinkedList底层是双向链表结构,属于顺序访问序列,ArrayList底层结构是数组,属于随机访问序列。LinkedList增删效率高,ArrayList改查效率高。存储同样大小的数据,LinkedList需要更多的内存。
3、对Hash函数的认识以及JDK1.8中hash算法的实现。
Hash函数要尽可能保证哈希松散,所谓哈希松散,就是指数值尽可能平均分布的hashCode,在Java语言中,一般会认为hashCode是一个int值,int是一个32bit整型数,比如一个8位16进制数1A54F2C0就比C8600000要松散。HashMap的默认容量是16,hashCode如果超过length-1,就会执行取余运算。如果一组数据其hashCode集中在000A0000-FFFF0000之间,那么计算indexFor时其取余结果全部为0。这种hashCode重复的现象称为哈希碰撞,当哈希碰撞发生,碰撞的键值对会被存入到同一条链表中,导致HashMap效率低下。松散哈西可以减少哈希碰撞。JDK1.8之前利用大量异或操作与无符号右移操作构建特定哈希松散算法hash(Object k),JDK1.8后,简化了哈希松散算法,之前版本多次位移异或并不能避免过多哈希碰撞,反倒增加了计算次数,Hashmap的效率问题主要集中在链表部分的遍历,这是一个经验性质的改进。
JDK1.8:(h = key.hashCode()) ^ (h >>> 16)
(1)首先获取对象的hashCode()值,然后将hashCode值右移16位,然后将右移后的值与原来的hashCode做异或运算,返回结果。(其中h>>>16,在JDK1.8中,优化了高位运算的算法,使用了零扩展,无论正数还是负数,都在高位插入0)。
(2)在putVal源码中,我们通过(n-1)&hash获取该对象的键在hashmap中的位置。(其中hash的值就是(1)中获得的值)其中n表示的是hash桶数组的长度,并且该长度为2的n次方,这样(n-1)&hash就等价于hash%n。因为&运算的效率高于%运算。
4、什么是Java中Collectors的fast-fail特性?
Java集合框架Iterator具有fast-fail的特性,在创建Iterator后,其它线程对Collection做了修改或当前线程调用非Iterator接口对集合做了修改,导致类似modCount!=expectedModCount,那么会抛出ConcurrentModificationException异常,产生fast-fail事件。
针对ArrayList,fast-fail的实现是在其父类AbastractList中,当执行add、remove、clear等方法时,modCount会发生改变,expectedodCount不变,调用checkForModification检测是否modCount==expectedModCount。而JUC中的CopyOnWriteArrayList没有继承AbstractList,仅仅实现了List接口,进行增删改时没有所谓checkForModification。