目录
3.为什么jdk1.6之前数组扩容是1.5倍加1,而jdk1.6之后是1.5倍
4.ArrayList的modcount的含义Iterator的fail-fast机制问题
5.ArrayList的安全机制,出现的问题,以及CopyOnWriteArrayList的实现原理,
1.ArrayList和LinkedList的区别
- ArrayList的底层是数组,所以在内存中的空间是连续的(指的是虚拟的空间)LinkedList的底层是双向链表
- 在ArrayList的中间插入元素不方便,而在LinkedList的中间插入或者删除元素方便
- LinkedList不支持高效的随机元素访问(第二点和第三点主要说数组和链表的区别即可)
- (理解,可以不用说给面试官)ArrayList的空间浪费主要体现在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间。
2.ArrayList的扩容
ArrayList和HashMap不同,没有扩容因子。当所需数组的长度大于现有数组的长度的时候,会进行扩容。扩容是将数组的长度扩大为原来的1.5倍
源码:
//minCapacity是基于已有元素的长度加1,表示数组需要的最少的容量 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //数组的长度变为原来的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); //判断新数组的长度是为了防止溢出的问题 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 拷贝数组 elementData = Arrays.copyOf(elementData, newCapacity); } |
3.为什么jdk1.6之前数组扩容是1.5倍加1,而jdk1.6之后是1.5倍
Jdk1.6之前ArrayList的初始化容量是10,扩容是原来的1.5倍加1,jdk1.6之后之后ArrayList的初始化容量是10,扩容是原来的1.5倍[可以这么说,因为jdk1.6的时候传参为空的时候就初始化数组的长度为10,而jdk1.7是初始化的长度为0,添加第一个元素的时候才将容量设置为10(取得Mincapacity和默认初始容量的最大值,然后再进行扩容)
为什么这样,百度给出的答案五花八门,我个人赞同下面这种说法:
jdk1.6初始化的时候刚开始的10,这样对于不需要长度为10的需求来说,刚开始创建10个容量是浪费空间的,而且位运算的效率远大于加减乘除,jdk1.6之后直接是右移,没有加1的运算,效率高
4.ArrayList的modcount的含义Iterator的fail-fast机制问题
表示从结构上修改此列表的次数,迭代的时候,假如使用了remove,add等方法,则会使用fail-fast机制抛出ConcurrentModificationException,或者是多线程对其进行读写操作的时候会抛出这个异常
5.ArrayList的安全机制,出现的问题,以及CopyOnWriteArrayList的实现原理,
ArrayList是线程不安全的,加入了CopyOnWriteArrayList解决线程安全的问题
这个时候CopyOnWriteArrayList 底层实现添加的原理是先copy出一个容器(可以简称副本),再往新的容器里添加这个新的数据,最后把新的容器的引用地址赋值给了之前那个旧的的容器地址,但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数据。
6.跳表的原理
在链表的基础上向上抽取索引实现的
其中,插入、删除、查找、迭代输出有序序列这几个操作,使用跳表实现和使用红黑树实现的时间复杂度是相同的,但是,按照区间查找数据,红黑树的效率没有跳表高。而且,跳表的实现要比红黑树简单。