首页 > 编程语言 >面试类-Java集合 (一)

面试类-Java集合 (一)

时间:2023-07-23 10:44:06浏览次数:32  
标签:遍历 Java LinkedList ArrayList 面试 数组 集合 序列化

1.说说有哪些常见集合?

集合相关类和接口都在java.util中,主要分为3种:List(列表)、Map(映射)、Set(集)。

Java集合主要关系

                                                                            Java集合主要关系

其中Collection是集合ListSet的父接口,它主要有两个子接口:

  • List:存储的元素有序,可重复。
  • Set:存储的元素不无序,不可重复。

Map是另外的接口,是键值对映射结构的集合。

List

List,也没啥好问的,但不排除面试官剑走偏锋,比如面试官也看了我这篇文章。

2.ArrayList和LinkedList有什么区别?

**(1)**数据结构不同

  • ArrayList基于数组实现
  • LinkedList基于双向链表实现

ArrayList和LinkedList的数据结构

                                          ArrayList和LinkedList的数据结构

(2) 多数情况下,ArrayList更利于查找,LinkedList更利于增删

  • ArrayList基于数组实现,get(int index)可以直接通过数组下标获取,时间复杂度是O(1);LinkedList基于链表实现,get(int index)需要遍历链表,时间复杂度是O(n);当然,get(E element)这种查找,两种集合都需要遍历,时间复杂度都是O(n)。

  • ArrayList增删如果是数组末尾的位置,直接插入或者删除就可以了,但是如果插入中间的位置,就需要把插入位置后的元素都向前或者向后移动,甚至还有可能触发扩容;双向链表的插入和删除只需要改变前驱节点、后继节点和插入节点的指向就行了,不需要移动元素。

ArrayList和LinkedList中间插入
                                         ArrayList和LinkedList中间插入

 

ArrayList和LinkedList中间删除

                                      ArrayList和LinkedList中间删除

注意,这个地方可能会出陷阱,LinkedList更利于增删更多是体现在平均步长上,不是体现在时间复杂度上,二者增删的时间复杂度都是O(n)

**(3)**是否支持随机访问

  • ArrayList基于数组,所以它可以根据下标查找,支持随机访问,当然,它也实现了RandmoAccess 接口,这个接口只是用来标识是否支持随机访问。
  • LinkedList基于链表,所以它没法根据序号直接获取元素,它没有实现RandmoAccess 接口,标记不支持随机访问。

**(4)**内存占用,ArrayList基于数组,是一块连续的内存空间,LinkedList基于链表,内存空间不连续,它们在空间占用上都有一些额外的消耗:

  • ArrayList是预先定义好的数组,可能会有空的内存空间,存在一定空间浪费
  • LinkedList每个节点,需要存储前驱和后继,所以每个节点会占用更多的空间

3.ArrayList的扩容机制了解吗?

ArrayList是基于数组的集合,数组的容量是在定义的时候确定的,如果数组满了,再插入,就会数组溢出。所以在插入时候,会先检查是否需要扩容,如果当前容量+1超过数组长度,就会进行扩容。

ArrayList的扩容是创建一个1.5倍的新数组,然后把原数组的值拷贝过去。

ArrayList扩容

                                              ArrayList扩容

4.ArrayList怎么序列化的知道吗? 为什么用transient修饰数组?

ArrayList的序列化不太一样,它使用transient修饰存储元素的elementData的数组,transient关键字的作用是让被修饰的成员属性不被序列化。

为什么最ArrayList不直接序列化元素数组呢?

出于效率的考虑,数组可能长度100,但实际只用了50,剩下的50不用其实不用序列化,这样可以提高序列化和反序列化的效率,还可以节省内存空间。

那ArrayList怎么序列化呢?

ArrayList通过两个方法readObject、writeObject自定义序列化和反序列化策略,实际直接使用两个流ObjectOutputStreamObjectInputStream来进行序列化和反序列化。

ArrayList自定义序列化
                                              ArrayList自定义序列化

5.快速失败(fail-fast)和安全失败(fail-safe)了解吗?

快速失败(fail—fast):快速失败是Java集合的一种错误检测机制

  • 在用迭代器遍历一个集合对象时,如果线程A遍历过程中,线程B对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。
  • 原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
  • 注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
  • 场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改),比如ArrayList 类。

安全失败(fail—safe)

  • 采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
  • 原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。
  • 缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
  • 场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改,比如CopyOnWriteArrayList类。

6.有哪几种实现ArrayList线程安全的方法?

fail-fast是一种可能触发的机制,实际上,ArrayList的线程安全仍然没有保证,一般,保证ArrayList的线程安全可以通过这些方案:

  • 使用 Vector 代替 ArrayList。(不推荐,Vector是一个历史遗留类)
  • 使用 Collections.synchronizedList 包装 ArrayList,然后操作包装后的 list。
  • 使用 CopyOnWriteArrayList 代替 ArrayList。
  • 在使用 ArrayList 时,应用程序通过同步机制去控制 ArrayList 的读写。

7.CopyOnWriteArrayList了解多少?

CopyOnWriteArrayList就是线程安全版本的ArrayList。

它的名字叫CopyOnWrite——写时复制,已经明示了它的原理。

CopyOnWriteArrayList采用了一种读写分离的并发策略。CopyOnWriteArrayList容器允许并发读,读操作是无锁的,性能较高。至于写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器。

CopyOnWriteArrayList原理

                                                                          CopyOnWriteArrayList原理

标签:遍历,Java,LinkedList,ArrayList,面试,数组,集合,序列化
From: https://www.cnblogs.com/pxzbky/p/17574764.html

相关文章

  • java调试技巧
    1.debug断点调试中,查看request中的parameter值一般需要打开request的7-9层才可以找到,(下图已经标上序号)打开第7层找到pathParameter,打开第9层找到parameter的值request->request->request->inputStream->ib->coyoteRequest->parameters->paramHashValues  参考:debug断点调......
  • JavaScript(二)运算符
    一:算数运算符+-*/加减乘除%取模(余数)二:赋值运算符+=-+*=/=%=letnum=5num=num+1num+=1两行一样三:自增自减运算符++  变量+1--  变量-1++在后面,先进行变量外的运算++在前面,先进行自增,在进行变量外的运算用的比较少四:比较运算符(关系运算符)输出布尔值><......
  • java正则表达式判断是否字母开头
    Java正则表达式判断是否字母开头介绍正则表达式是一种用来描述和匹配字符串模式的工具。在Java中,我们可以使用正则表达式来进行字符串的匹配和搜索操作。本文将教会你如何使用Java正则表达式来判断一个字符串是否以字母开头。流程下面是实现“Java正则表达式判断是否字母开头”......
  • java正则表达式不包含
    如何在Java中使用正则表达式进行不包含匹配引言正则表达式是一种强大的文本匹配工具,它可以帮助我们在字符串中查找、替换或验证特定的模式。在Java中,我们可以使用java.util.regex包中的类来操作正则表达式。本文将教会你如何在Java中使用正则表达式进行不包含匹配。流程图以下......
  • java正则表达式包含数字字母
    实现"Java正则表达式包含数字字母"1.概览在本文中,我将教你如何使用Java正则表达式来判断一个字符串是否包含数字和字母。我们将按照以下步骤进行操作:创建一个正则表达式模式,用于匹配包含数字和字母的字符串。创建一个Pattern对象,用于编译正则表达式。创建一个Matcher对象,用......
  • java正则表达式 不以数字开头输出
    Java正则表达式:不以数字开头输出介绍正则表达式是一种强大的模式匹配工具,可以用来在文本中查找、替换、拆分等操作。在Java中,可以使用java.util.regex包中的类来操作正则表达式。本文将介绍如何使用正则表达式来判断一个字符串是否不以数字开头,并提供代码示例。正则表达式语法......
  • java正则 只能为某字符串值输入
    Java正则表达式:只能为某字符串值输入导言在日常的编程工作中,我们常常需要对输入进行验证。例如,我们可能只希望用户输入特定的字符串值。为了实现这个功能,我们可以使用Java正则表达式。正则表达式是一种强大的模式匹配工具,可以用来在字符串中查找、替换、验证等操作。在Java中,我......
  • 老杜 JavaWeb 讲解(十三) ——JSP简单了解
    (十四)JSP相关视频:35-JSP原理深度解析36-JSP的各种基础语法37-JSP的输出语法第一个JSP程序在WEB-INF目录之外创建一个index.jsp文件,然后这个文件中没有任何内容。将上面的项目部署之后,启动服务器,打开浏览器,访问以下地址:http://localhost:8080/jsp/index.jsp展现......
  • java list 随机排序
    java list随机排序 packagecom.vfsd.test;importjava.util.ArrayList;importjava.util.Collection;importjava.util.Collections;importjava.util.List;importjava.util.stream.Collectors;publicclassTest_List_Shuffle{publicstaticvoidma......
  • 10道SpringBoot面试题
    SpringBoot我是JavaPub,专注于面试、副业,技术人的成长记录。以下是SpringBoot面试题,相信大家都会有种及眼熟又陌生的感觉、看过可能在短暂的面试后又马上忘记了。JavaPub在这里整理这些容易忘记的重点知识及解答,建议收藏,经常温习查阅。评论区见本系列《最少必要面试题》Sprin......