Java集合详解12:arraylist和linkedlist面试题汇总,可能没有你想象中那么简单!

Posted 黄小斜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java集合详解12:arraylist和linkedlist面试题汇总,可能没有你想象中那么简单!相关的知识,希望对你有一定的参考价值。

ArrayList 无参数构造器构造,现在 add 一个值进去,此时数组的大小是多少,下一次扩容前最大可用大小是多少?
此处数组的大小是 1,下一次扩容前最大可用大小是 10,因为 ArrayList 第一次扩容时,是有默认值的,默认值是 10,在第一次 add 一个值进去时,数组的可用大小被扩容到 10 了。

如果我连续往 Arraylist 里面新增值,增加到第 11 个的时候,数组的大小是多少?
这里的考查点就是扩容的公式,当增加到 11 的时候,此时我们希望数组的大小为 11,但实际上数组的最大容量只有 10,不够了就需要扩容,扩容的公式是:oldCapacity + (oldCapacity>> 1),oldCapacity 表示数组现有大小,目前场景计算公式是:10 + 10 /2 = 15,然后我们发现 15 已经够用了,所以数组的大小会被扩容到 15。

数组初始化,被加入一个值后,如果我使用 addAll 方法,一下子加入 15 个值,那么最终数组的大小是多少?
现在需要一下子加入 15 个值,那我们期望数组的大小值就是 16,此时数组最大可用大小只有 10,明显不够,需要扩容,扩容后的大小是:10 + 10 /2 = 15,这时候发现扩容后的大小仍然不到我们期望的值 16,这时候源码中有一种策略如下:

// newCapacity 本次扩容的大小,minCapacity 我们期望的数组最小大小
// 如果扩容后的值 < 我们的期望值,我们的期望值就等于本次扩容的大小
if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
1
2
3
4
所以最终数组扩容后的大小为 16。

现在我有一个很大的数组需要拷贝,原数组大小是 5k,请问如何快速拷贝?
因为原数组比较大,如果新建新数组的时候,不指定数组大小的话,就会频繁扩容,频繁扩容就会有大量拷贝的工作,造成拷贝的性能低下,所以回答说新建数组时,指定新数组的大小为 5k 即可。

为什么说扩容会消耗性能?
扩容底层使用的是 System.arraycopy 方法,会把原数组的数据全部拷贝到新数组上,所以性能消耗比较严重。

有一个 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);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
不能删除干净,最终删除的结果是 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 和 LinkedList 是如何对 null 值进行处理的
ArrayList 允许 null 值新增,也允许 null 值删除。删除 null 值时,是从头开始,找到第一值是 null 的元素删除;LinkedList 新增删除时对 null 值没有特殊校验,是允许新增和删除的。

ArrayList 和 LinedList 是线程安全的么,为什么?
当两者作为非共享变量时,比如说仅仅是在方法里面的局部变量时,是没有线程安全问题的,只有当两者是共享变量时,才会有线程安全问题。主要的问题点在于多线程环境下,所有线程任何时刻都可对数组和链表进行操作,这会导致值被覆盖,甚至混乱的情况。

如何解决线程安全问题
Java 源码中推荐使用 Collections#synchronizedList 进行解决,Collections#synchronizedList 的返回值是 List 的每个方法都加了 synchronized 锁,保证了在同一时刻,数组和链表只会被一个线程所修改,或者采用 CopyOnWriteArrayList 并发 List 来解决

描述下双向链表的新增和删除
————————————————
版权声明:本文为CSDN博主「FuYouJ」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Fujie1997/article/details/105207966/
 

微信公众号【程序员书单】

一个为程序员推荐好书的公众号。每周为你pick精品书单,技术好文,优质学习资源和工具软件。关注Java、Python、大数据、AI等技术与行业资讯,更关注你的自我提升。人生苦短,要把时间浪费在美好的事物上~回复“book”即可领取java后端学习必备20+本电子书。更多电子书下载,请移步至程序员书单官网:coderbooklist.com

微信公众号【Java技术江湖】

一位阿里 Java 工程师的技术小站,专注于 Java 相关技术:SSM、SpringBoot、mysql、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!关注公众号后回复“PDF”即可领取200+页的《Java工程师面试指南》强烈推荐,几乎涵盖所有Java工程师必知必会的知识点。

以上是关于Java集合详解12:arraylist和linkedlist面试题汇总,可能没有你想象中那么简单!的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中ArrayList集合详解

JAVA容器之ArrayList集合详解

java集合List集合之ArrayList详解

Java ArrayList排序方法详解

java集合框架详解

java集合类之ArrayList详解