当前位置: 首页 > 科技观察

面试必备:30道Java集合面试题及答案

时间:2023-03-20 19:32:07 科技观察

Java集合框架是Java编程语言的基础,也是Java面试中非常重要的知识点。在这里,我列出了一些关于Java集合的重要问题和答案。1、什么是Java集合框架?说出集合框架的一些优点?集合存在于每种编程语言中,最初的Java版本包含几个集合类:Vector、Stack、HashTable和Array。随着集合的广泛使用,Java1.2提出了集合框架,包括所有的集合接口、实现和算法。Java在确保线程安全的同时,在使用泛型和并发集合类方面取得了长足的进步。它还在Java并发包中包括阻塞接口及其实现。集合框架的一些优点如下:通过使用核心集合类而不是实现我们自己的集合类来降低开发成本。使用经过良好测试的集合框架类可以提高代码质量。使用JDK自带的集合类可以降低代码维护成本。可重用性和可操作性。2.泛型在集合框架中有什么优势?Java1.5引入了泛型,所有集合接口和实现都广泛使用它。泛型允许我们为集合提供它可以容纳的对象类型,因此如果您添加任何其他类型的元素,它将在编译时抛出错误。这避免了在运行时出现ClassCastException,因为您将在编译时收到错误消息。泛型还使代码更简洁,我们不需要使用显式强制转换和instanceOf运算符。它还具有运行时优势,因为不会生成经过类型检查的字节码指令。3、Java集合框架有哪些基本接口?集合是集合层次结构的根接口。集合表示一组对象,这些对象是它的元素。Java平台不提供此接口的任何直接实现。Set是一个不能包含重复元素的集合。此接口对数学集合抽象进行建模,并用于表示集合,如一副纸牌。List是一个有序的集合,可以包含重复的元素。您可以通过其索引访问任何元素。List更像是一个长度动态变化的数组。Map是将键映射到值的对象。Map不能包含重复的键:每个键最多可以映射一个值。其他一些接口是Queue、Dequeue、SortedSet、SortedMap和ListIterator。4、为什么Collection不继承Cloneable和Serializable接口?Collection接口指定了一组对象,这些对象是它的元素。如何维护这些元素是由Collection的具体实现决定的。例如,某些Collection实现(例如List)允许重复元素,而其他实现(例如Set)则不允许。许多Collection实现都有一个公共的克隆方法。但是,将它放在集合的所有实现中也没有意义。这是因为Collection是一种抽象表示。重要的是执行。在处理具体实现时,克隆或序列化的语义和意义开始发挥作用。因此,实现应该决定如何克隆或序列化它,或者它是否可以被克隆或序列化。点此阅读一篇学习连载的文章。在所有实现中授权克隆和序列化,最终导致灵活性降低和限制增加。具体的实现应该决定它是否可以被克隆和序列化。点此阅读一篇学习连载的文章。5、为什么Map接口不继承Collection接口?尽管Map接口及其实现是集合框架的一部分,但Map不是集合,集合也不是Map。所以Map继承Collection没有意义,反之亦然。Map如果继承了Collection接口,元素到哪里去了?Map包含键值对,提供提取键或值列表集合的方法,但不符合“对象集”规范。6.什么是迭代器?Iterator接口提供了一个用于遍历任何Collection的接口。我们可以使用iterator方法从Collection中获取迭代器实例。迭代器取代了Java集合框架中的枚举。迭代器允许调用者在迭代期间删除元素。7.Enumeration和Iterator接口有什么区别?Enumeration的速度是Iterator的两倍,而且使用的内存更少。枚举很基础,满足基本需求。但是,与Enumeration相比,Iterator更安全,因为在遍历集合时,它可以防止其他线程修改集合。迭代器取代了Java集合框架中的枚举。迭代器允许调用者从集合中移除元素,而枚举不能。改进了迭代器方法名称,使其功能更清晰。8、为什么没有像Iterator.add()这样的方法向集合中添加元素?语义不清楚。众所周知,Iterator协议不保证迭代的顺序。但是请注意,ListIterator不提供add操作,它可以确保迭代的顺序。9、为什么迭代器没有不移动光标直接获取下一个元素的方法?它可以在当前Iterator的顶层实现,但很少被使用。如果加在接口上,每一次继承都必须实现它,没有意义。10.Iterator和ListIterator有什么区别?(1)我们可以使用Iterator来遍历Set和List集合,而ListIterator只能遍历List。(2)Iterator只能向前遍历,而LIstIterator可以双向遍历。(3)ListIterator继承了Iterator接口,然后增加了一些额外的功能,比如添加一个元素,替换一个元素,获取前一个或后一个元素的索引位置。11.通过迭代器的fail-fast属性,你理解什么?每次我们尝试获取下一个元素时,Iteratorfail-fast属性都会检查当前集合结构中的任何更改。如果发现任何修改,它会抛出ConcurrentModificationException。Collection中的所有Iterator实现都设计为快速失败(并发集合类除外,例如ConcurrentHashMap和CopyOnWriteArrayList)。12.fail-fast和fail-safe有什么区别?Iterator的fail-fast属性适用于当前集合,因此它不会受到集合中任何更改的影响。java.util包中的所有集合类都设计为fail-fast,而java.util.concurrent中的集合类都是fail-safe。Fall——快速迭代器抛出ConcurrentModificationException,fall——安全迭代器从不抛出ConcurrentModificationException。13.如何避免遍历集合?并发修改异常?我们可以使用并发集合类来避免在遍历集合时出现ConcurrentModificationException,比如使用CopyOnWriteArrayList而不是ArrayList。14、为什么没有Iterator接口的具体实现?Iterator接口定义了遍历集合的方法,但它的实现是集合实现类的责任。每个返回Iterator进行遍历的集合类都有自己的Iterator实现内部类。这允许集合类选择迭代器是快速故障还是故障安全的。例如,ArrayList迭代器是快速失败的,而CopyOnWriteArrayList迭代器是失败安全的。15.什么是UnsupportedOperationException?UnsupportedOperationException是一个异常,用于表示不支持该操作。它已广泛用于JDK类中。在集合框架中,java.util.Collections.UnmodifiableCollection将在所有添加和删除操作中抛出此异常。16.hashCode()和equals()方法的重要性是什么?HashMap使用Key对象的hashCode()和equals()方法来确定键值对的索引。点击此处了解它们之间的关系。当我们尝试从HashMap中获取值时,也会用到这些方法。如果这些方法没有正确实现,在这种情况下,两个不同的Key可能会产生相同的hashCode()和equals()输出,HashMap会认为它们相同并覆盖它们,而不是将它们存储在不同的地方。此外,所有不允许重复的集合类都使用hashCode()和equals()来查找重复项,因此正确实现它们很重要。equals()和hashCode()的实现应遵循以下规则:如果o1.equals(o2),则o1.hashCode()==o2.hashCode()始终为真。如果o1.hashCode()==o2.hashCode(),并不意味着o1.equals(o2)为真。17、Map接口提供了哪些不同的集合视图?Map接口提供了三种集合视图:1)Setkeyset():返回映射中包含的所有键的集合视图。集合由地图支持,对地图的更改反映在集合中,反之亦然。当迭代器遍历集合时,如果映射被修改(除了迭代器自己的删除操作),迭代器的结果将变为未定义。集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作移除元素,从map中移除对应的映射。它不支持add和addAll操作。2)Collectionvalues():返回一个map中包含的所有值的Collection视图。此集合由地图支持,对地图的更改将反映在集合中,反之亦然。当迭代器遍历集合时,如果映射被修改(除了迭代器自己的删除操作),迭代器的结果将变为未定义。集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作移除元素,从map中移除对应的映射。它不支持add和addAll操作。3)Set>entrySet():返回一个地图时钟中包含的所有映射的集合视图。此集合由地图支持,对地图的更改将反映在集合中,反之亦然。迭代器在遍历集合时,如果修改了map(除了迭代器本身的remove操作,以及迭代器返回的entry的setValue),迭代器的结果就会变成undefined。集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作移除元素,从map中移除对应的映射。它不支持add和addAll操作。18.HashMap和HashTable有什么区别?(1)HashMap允许key和value为null,而HashTable不允许。(2)HashTable是同步的,HashMap不是。所以HashMap适合单线程环境,HashTable适合多线程环境。(3)LinkedHashMap,HashMap的子类,在Java1.4中引入。如果要遍历顺序,可以很方便地从HashMap切换到LinkedHashMap,但是HashTable不是这样的,它的顺序是不可预测的。(4)HashMap提供keySet的遍历,所以是fail-fast,而HashTable提供keyEnumeration的遍历,不支持fail-fast。(5)HashTable被认为是一个遗留类,如果你在迭代时寻求修改Map,你应该使用CocurrentHashMap。19.如何决定选择HashMap还是TreeMap?对于Map中的插入、删除、定位元素等操作,HashMap是最佳选择。但是,如果需要遍历有序的键集合,TreeMap是更好的选择。根据集合的大小,将元素添加到HashMap中并用TreeMap替换映射以进行有序键遍历可能会更快。20.ArrayList和Vector有什么异同?ArrayList和Vector在很多情况下非常相似。(1)两者都是基于索引的,内部由数组支持。(2)两者都维护插入的顺序,我们可以按照插入的顺序获取元素。(3)ArrayList和Vector的迭代器实现是快速失败的。(4)ArrayList和Vector都允许空值,也可以使用索引值对元素进行随机访问。以下是ArrayList和Vector之间的区别。(1)Vector是同步的,ArrayList不是。但是,如果您希望在迭代时对列表进行更改,则应使用CopyOnWriteArrayList。(2)ArrayList比Vector快,因为它有同步,不会被重载。(3)ArrayList更通用,因为我们可以使用Collections工具类轻松获取同步列表和只读列表。21.Array和ArrayList有什么区别?什么时候用Array比较合适?Array可以容纳基本类型和对象,而ArrayList只能容纳对象。Array是指定大小的,而ArrayList大小是固定的。Array没有ArrayList提供那么多的函数,比如addAll、removeAll和iterator。尽管ArrayList显然是更好的选择,但有时Array更好用。(1)如果已经指定了链表的大小,大多数情况下是存储和遍历。(2)对于遍历原始数据类型,虽然Collections使用自动装箱来简化编码任务,但处理指定大小的原始类型列表也会变得非常慢。(3)如果要使用多维数组,使用[][]比使用List>更方便。22.ArrayList和LinkedList有什么区别?ArrayList和LinkedList都实现了List接口,但它们之间存在一些差异。1)ArrayList是Array支持的基于索引的数据结构,所以它提供了对元素的随机访问,复杂度为O(1),而LinkedList存储的是一系列的节点数据,每个节点都与前一个节点相关,并与前一个节点相连下一个节点。所以,虽然有使用索引获取元素的方法,但是内部实现是从起点遍历,遍历到索引的节点,然后返回元素。时间复杂度为O(n),比ArrayList慢。2)在LinkedList中插入、添加和删除元素比ArrayList更快,因为它不涉及改变数组的大小,或者在中间插入元素时更新索引。3)LinkedList比ArrayList更耗内存,因为LinkedList中的每个节点都存储了前后节点的引用。23.哪些集合类提供对元素的随机访问?ArrayList、HashMap、TreeMap和HashTable类提供对元素的随机访问。24.哪些集合类是线程安全的?Vector、HashTable、Properties、Stack都是同步类,因此是线程安全的,可以在多线程环境下使用。Java1.5并发API包括一些允许在迭代期间修改的集合类,并且因为它们都在集合克隆上工作,所以它们在多线程环境中是安全的。单击此处查看文章以了解什么线程是不安全的。25.什么是并发集合类?Java1.5并发包(java.util.concurrent)包含线程安全的集合类,允许在迭代时修改集合。迭代器被设计为快速失败并且会抛出ConcurrentModificationException。一些类是:CopyOnWriteArrayList、ConcurrentHashMap、CopyOnWriteArraySet。26.什么是队列和栈,并列出它们的区别?栈和队列都是用来预存数据的。java.util.Queue是一个接口,它的实现类在Java并发包中。队列允许对元素进行先进先出(FIFO)检索,但并非总是如此。Deque接口允许从两端检索元素。堆栈类似于队列,但它允许后进先出(LIFO)检索元素。Stack是从Vector扩展的类,而Queue是一个接口。27.什么是Collections类?java.util.Collections是一个实用程序类,仅包含操作或返回集合的静态方法。它包含用于操作集合、返回由指定集合支持的新集合等的多态算法。此类包含集合框架算法的方法,例如二进制搜索、排序、混洗和反转。28.Comparable和Comparator接口有什么区别?Comparable和Comparator接口用于对对象的集合或数组进行排序。Comparable接口用于提供对象的自然排序,我们可以使用它来提供基于单一逻辑的排序。Comparator接口用于提供不同的排序算法,我们可以选择我们需要使用的Comparator来对给定的对象集合进行排序。29.我们如何对一组对象进行排序?如果我们需要对对象数组进行排序,可以使用Arrays.sort()方法。如果我们需要对对象列表进行排序,我们可以使用Collection.sort()方法。这两个类都有一个重载方法sort()用于自然排序(使用Comparable)或基于条件的排序(使用Comparator)。Collections内部使用数组排序的方式,所以除了Collections需要时间将列表转换为数组外,两者的性能是一样的。30.当集合作为参数传递给函数时,如何保证函数不能修改它?我们可以使用Collections.unmodifiableCollection(Collectionc)方法在将其作为参数传递之前创建一个只读集合,这将确保任何改变集合的操作都会抛出UnsupportedOperationException。