赞
踩
JDK1.7
中,使用ArrayList list = new ArrayList()
创建List集合
时,底层直接创建了长度是10的 Object[]
数组 elementData
;在接下来调用add()
方法向集合中添加元素时,如果本次的添加导致底层elementData
数组容量不足,则进行扩容。默认情况下,扩容为原来的1.5
倍(>>1
),同时将原来数组中的所有数据复制到新的数组中。JDK1.8
中,使用ArrayList list = new ArrayList()
创建List集合
时,底层的Object[] elementData
初始化为{}
,并没有直接创建长度为10的数组
;而在第一次调用add()方法时,底层才创建了长度为10的数组,并将本次要添加的元素添加进去
。ArrayList
无参数构造构造器构造,现有一个add
一个值,此时的数组大小是多少,下一次扩容前最大可用大小是多少?答: 此处数组的大小是 1,下一次扩容前最大可用大小是 10,因为 ArrayList
第一次扩容时,是有默认值的,默认值是 10,在第一次add
一个值进去时,数组的可用大小被扩容到 10 了。
List
里新增值,增加到第11个时,数组大小是多少?答:这里的考查扩容的公式,当增加到 11 的时候,此时我们希望数组的大小为 11,但实际上数组的最大容量只有 10,不够了就需要扩容,扩容的公式是:oldCapacity + (oldCapacity>> 1),oldCapacity
表示数组现有大小,目前场景计算公式是:10 + 10 / 2 = 15
,然后我们发现 15 已经够用了,所以数组的大小会被扩容到 15。
addAll
方法,一下子加入 15 个值,那么最终数组的大小是多少?答:第一题中我们已经计算出来数组在加入一个值后,实际大小是 1,最大可用大小是 10 ,现在需要一下子加入 15 个值,那我们期望数组的大小值就是 16,此时数组最大可用大小只有 10,明显不够,需要扩容,扩容后的大小是:10 + 10 / 2 = 15
,这时候发现扩容后的大小仍然不到我们期望的值 16,这时候源码中有一种策略如下:
// newCapacity 本次扩容的大小,minCapacity 我们期望的数组最小大小
// 如果扩容后的值 < 我们的期望值,我们的期望值就等于本次扩容的大小
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
答:因为原数组较大,如果新建数组的时候,不指定数组大小的话,就会频繁扩容,频繁扩容就会有大量拷贝的工作,造成拷贝的性能低,所以回答说新建数组时,指定**新数组的大小为 5k **即可。
答:扩容底层使用的是 System.arraycopy
方法,会把原数组的数据全部拷贝到新数组上,所以性能消耗比较严重。
答:
Integer
的最大值。 ArrayList
,数据是 2、3、3、3、4
,中间有三个 3,现在我通过for (int i=0;i<list.size ();i++)
的方式,想把值是 3 的元素删除,请问可以删除干净么?最终删除的结果是什么,为什么?List<String> list = new ArrayList<String>() {{
add("2");
add("3");
add("3");
add("3");
add("4");
}};
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("3")) {
list.remove(i);
}
}
答:不能删除干净,最终删除的结果是 2、3、4,有一个 3 删除不掉,原因我们看下图
ArrayList
数组,我们通过增强 for
循环进行删除,可以么?答: 不可以。因为增强 for
循环过程是调用迭代器的 next ()
方法,当你调用 List
的remove ()
方法进行删除时,modCount
的值会 +1,而这时候迭代器中的 expectedModCount
的值却没有变,导致在迭代器下次执行 next ()
方法时,expectedModCount != modCount
就会报 ConcurrentModificationException
的错误。
Iterator.remove ()
方法可以删除么,为什么?答: 可以的,因为Iterator.remove ()
方法在执行的过程中,会把最新的 modCount
赋值给 expectedModCount
,这样在下次循环过程中,modCount
和expectedModCount
两者就会相等。
LinkedList
也是同样的结果么?答:是的,虽然 LinkedList
底层结构是双向链表,但对于上述三个问题,结果和 ArrayList
是一致的。
ArrayList
和 LinkedList
有何不同?答: 可以先从底层数据结构开始说起,然后以某一个方法为突破口深入,比如:最大的不同是两者底层的数据结构不同,ArrayList
底层是数组,LinkedList
底层是双向链表,两者的数据结构不同也导致了操作的 API
实现有所差异,拿新增实现来说,ArrayList
会先计算并决定是否扩容,然后把新增的数据直接赋值到数组上,而 LinkedList
仅仅只需要改变插入节点和其前后节点的指向位置关系即可。
ArrayList
和 LinkedList
应用场景有何不同?答: ArrayList
更适合于快速的查找匹配,不适合频繁新增删除,像工作中经常会对元素进行匹配查询的场景比较合适,LinkedList
更适合于经常新增和删除,对查询反而很少的场景。
ArrayList
和LinkedList
两者有没有最大容量?答: ArrayList
有最大容量,为 Integer
的最大值,大于这个值JVM
是不会为数组分配内存空间的,LinkedList
底层是双向链表,理论上可以无限大。但源码中,LinkedList
实际大小用的是 int
类型,这也说明了 LinkedList
不能超过 Integer
的最大值,不然会溢出。
答: ArrayList
和 LinkList
都允许对null进行新增和删除操作。ArrayList
允许 null
值新增,也允许 null
值删除。删除 null
值时,是从头开始,找到第一值是 null
的元素删除;LinkedList
新增删除时对 null
值没有特殊校验,是允许新增和删除的。
ArrayList
和 LinedList
是线程安全的么,为什么?答:当两者作为非共享变量时,比如说仅仅是在方法里面的局部变量时,是没有线程安全问题的,只有当两者是共享变量时,才会有线程安全问题。主要的问题点在于多线程环境下,所有线程任何时刻都可对数组和链表进行操作,这会导致值被覆盖,甚至混乱的情况。会频繁报 ConcurrentModificationException
的错误。
答:Java 源码中推荐使用 Collections
加synchronizedList
进行解决,Collections
加synchronizedList
的返回值是 List
的每个方法都加了 synchronized
锁,保证了在同一时刻,数组和链表只会被一个线程所修改,或者采用 CopyOnWriteArrayList
并发 List
来解决,这个类我们后面会说。
答:双向链表中双向的意思是说前后节点之间互相有引用,链表的节点我们称为 Node
。Node
有三个属性组成:其前一个节点,本身节点的值,其下一个节点,假设 A
、B
节点相邻,A
节点的下一个节点就是 B
,B
节点的上一个节点就是 A
,两者互相引用,在链表的头部节点,我们称为头节点。头节点的前一个节点是 null
,尾部称为尾节点,尾节点的后一个节点是null
,如果链表数据为空的话,头尾节点是同一个节点,本身是 null
,指向前后节点的值也是 null
。
答:
prev
指向其前一个节点,把删除节点的前一个节点的 next
指向其后一个节点,最后把删除的节点置为 null
即可。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。