每日面试题打卡(容器篇)——Day7

博主个人博客网站:文客
这个系列会长期更新!
如果你想每天和我打卡面试题、交流技术,可以关注一下我的个人博客网站:文客,我会每天在这里更新技术文章和面试题,也会及时收到大家的评论与留言,欢迎各位大佬来交流!

迭代器 Iterator 是什么?怎么使用?有什么特点?

迭代器Iterator是Java提供的接口,它可以遍历任何Collection接口的实现类。我们可以从一个Collection中使用迭代器方法来获取迭代器实例。Iterator的特点是只能单向遍历,而且允许调用者在遍历过程中移除元素,所以它更加安全,他可以确保当前遍历的集合元素被更改时,会抛出ConcurrentModificationException。

如何边遍历边移除 Collection 中的元素?

唯一方式就是使用Iterator.remove()方法,比如:

Iterator it = list.iterator();
whlie(it.hasNext()){
	it.remove();
}

这里也要说一下很容易犯的错误,是这样写的:

for(Integer i : list){
	list.remove(i);
}

运行上面这段代码就会触发ConcurrentModificationException异常。这是因为当使用foreach进行遍历时,会自动生成一个iterator进行遍历,但同时该list又被remove操作所修改,Java一般不会允许一个线程在遍历Collection时被另外一个线程所修改。

Iterator 和 ListIterator 有什么区别?

  • Iterator可以遍历Set和List集合,而ListIterator只能遍历 List。
  • Iterator只能单向遍历,而ListIterator可以双向遍历(向前/后遍历)。
  • ListIterator实现Iterator接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

遍历一个 List 有哪些不同的方式?每种方法的实现原理是什么?Java 中 List 遍历的最佳实践是什么?

通常我们有三种遍历集合的方式:

  1. for循环遍历,基于计数器。在集合外部维护一个计数器,然后依次读取每一个位置的元素,当读取到最后一个元素后停止。
  2. 迭代器遍历,Iterator。Iterator是面向对象的一个设计模式,目的是屏蔽不同数据集合的特点,统一遍历集合的接口。Java在 Collections中支持了Iterator模式。
  3. foreach循环遍历。foreach内部也是采用了Iterator的方式实现,使用时不需要显式声明Iterator或计数器。优点是代码简洁,不易出错;缺点是只能做简单的遍历,不能在遍历过程中操作数据集合,例如删除、替换。

那么如何确定遍历的最佳实践呢?

Java Collection框架中提供了一个RandomAccess接口,用来标记List的实现类是否支持Random Access,Random Access就是随机访问,那我们就可以根据这个标签来决定遍历的最佳实现了,比如:

  • ArrayList就实现了RandomAccess接口,而且从底层数据结构我们也能看出,ArrayList是支持随机访问的,所以推荐ArrayList使用for循环这种方式来遍历
  • LinkedList同ArrayList一样,也是List的实现类,但是它没有实现RandomAccess接口,所以建议使用Iterator或者foreach遍历

说一下 ArrayList 的优缺点

优点:

  1. 实现了RandomAccess接口,查询效率很高
  2. 顺序添加一个元素时非常方便

缺点:

  1. 在指定位置插入元素时,会有一个arraycopy操作,如果要复制的元素很多,会比较影响性能
  2. 在指定位置删除元素时,同样会有arraycopy操作,也会影响性能

如何实现数组和 List 之间的转换?

数组转List:使用Arrays.asList(array)

List转数组:使用List自带的toArray()方法

插入数据时,ArrayList、LinkedList、Vector谁速度较快?阐述 ArrayList、Vector、LinkedList 的存储性能和特性?

由于Vector是线程安全的,所以性能上要差一些。我们着重比较ArrayList和LinkedList。

先看一下它们插入数据的底层方法:

  • ArrayList在插入元素时,会有扩容操作,这是影响性能的一个因素
  • LinkedList在插入元素时,每次都会new一个新节点,这也是影响性能的一个因素

单单从源码层面无法评估,我曾做过一个实验,在数据量比较小,30w以下时,LinkedList比较快;然而在数据量比较大时ArrayList会更快,这和ArrayList的扩容机制和LinkedList的插入特点有关,LinkedList每次插入都会new一个新的节点,在数据量小的时候,造成的影响可能不是很明显,而当数据量小时,ArrayList扩容的次数就比较多,时间上的开销可能会大一些。当数据量大的时候,new一个节点对性能的影响开始逐渐明显,而这时ArrayList扩容操作变少,效率就有了很大的提升,所以当数据量越来越大时,ArrayList的效率提升也会越来越高。

多线程场景下如何使用 ArrayList?

ArrayList不是线程安全的,如果遇到多线程场景,可以通过Collections的synchronizedList方法将其转换成线程安全的容器后再使用。

为什么 ArrayList 的 elementData 加上 transient 修饰?

ArrayList是支持序列化的,而transient的作用是不想让elementData被序列化,这是为了提高序列化的性能,每次序列化时,先调用 defaultWriteObject()方法序列化ArrayList中的非transient修饰的元素,然后遍历elementData,只序列化已存入的元素,这样既加快了序列化的速度,又减小了序列化之后的文件大小。

讨论:单次插入的情况下,ArrayList和LinkedList谁更快?

博客原文地址

每日面试题打卡(容器篇)——Day7
在这里插入图片描述

你可能感兴趣的:(面试题打卡,java,面试,容器)