40 个 Java 集合面试问题和答案(上)

前言:

Java 集合框架是 Java 编程语言的基本方面。这是java面试问题的重要主题之一。在这里,我列出了一些关于java 集合框架的重要问题和答案。

1.什么是 Java 集合框架?列出 Collections 框架的一些好处?

集合用于每种编程语言,最初的 Java 版本包含几个集合类:VectorStackHashtable 、Array。但是从更大的范围和使用情况来看,Java 1.2 提出了集合框架,它将所有集合接口、实现和算法分组。Java 集合在使用泛型和并发集合类进行线程安全操作方面取得了长足的进步。它还包括阻塞接口及其在 java 并发包中的实现。集合框架的一些好处是:

通过使用核心集合类而不是实现我们自己的集合类来减少开发工作。

使用经过良好测试的集合框架类可以提高代码质量。

通过使用 JDK 附带的集合类来减少代码维护工作。

可重用性和互操作性

2.集合框架中的泛型有什么好处?

Java 1.5 带有泛型,所有集合接口和实现都大量使用它。泛型允许我们提供集合可以包含的 Object 类型,因此如果您尝试添加任何其他类型的元素,则会引发编译时错误。这避免了运行时的 ClassCastException,因为您将在编译时收到错误。由于我们不需要使用强制转换和 instanceof 运算符,泛型也使代码更干净。它还增加了运行时的好处,因为不会生成进行类型检查的字节码指令。

3.Java Collections Framework 的基本接口是什么?

集合是集合层次结构的根。集合表示一组称为元素的对象。Java 平台不提供此接口的任何直接实现。

Set是一个不能包含重复元素的集合。该接口对数学集合抽象进行建模,并用于表示集合,例如一副纸牌。

List是一个有序的集合,可以包含重复的元素。您可以从它的索引访问任何元素。List 更像是具有动态长度的数组。

Map是将键映射到值的对象。映射不能包含重复的键:每个键最多可以映射到一个值。

其他一些接口是Queue、Dequeue、Iterator、SortedSet和。SortedMapListIterator

4.为什么 Collection 不扩展 Cloneable 和 Serializable 接口?

集合接口指定一组称为元素的对象。如何维护元素取决于 Collection 的具体实现。例如,某些 Collection 实现(如 List)允许重复元素,而其他实现(如 Set)则不允许。许多 Collection 实现都有一个公共的克隆方法。但是,将它包含在 Collection 的所有实现中并没有真正的意义。这是因为 Collection 是一种抽象表示。重要的是执行。
在处理实际实现时,克隆或序列化的语义和含义会发挥作用;所以具体的实现应该决定它应该如何被克隆或序列化,或者即使它可以被克隆或序列化。
因此,在所有实现中强制克隆和序列化实际上不太灵活且限制性更强。具体的实现应该决定它是否可以被克隆或序列化。

5.为什么 Map 接口不扩展 Collection 接口?

尽管 Map 接口及其实现是 Collections Framework 的一部分,但 Map 不是集合,集合也不是 Map。因此 Map 扩展 Collection 没有意义,反之亦然。
如果 Map 扩展了 Collection 接口,那么元素在哪里?Map 包含键值对,它提供了检索键或值列表作为集合的方法,但它不适合“元素组”范式。

6.什么是迭代器?

Iterator 接口提供了迭代任何 Collection 的方法。我们可以使用迭代器方法从集合中获取迭代器实例。迭代器在 Java 集合框架中取代了枚举。迭代器允许调用者在迭代期间从底层集合中移除元素。

7.枚举和迭代器接口有什么区别?

枚举的速度是迭代器的两倍,并且使用的内存非常少。枚举是非常基础的,适合基本需求。但是 Iterator 比 Enumeration 更安全,因为它总是拒绝其他线程修改它正在迭代的集合对象。
迭代器在 Java 集合框架中取代了枚举。迭代器允许调用者从底层集合中删除元素,而枚举是不可能的。迭代器方法名称已得到改进,使其功能清晰。

8.为什么没有像 Iterator.add() 这样的方法来向集合中添加元素?

语义尚不清楚,因为 Iterator 的合同不保证迭代的顺序。但是请注意,ListIterator 确实提供了添加操作,因为它确实保证了迭代的顺序。

9.为什么迭代器没有直接获取下一个元素而不移动光标的方法?

它可以在当前的 Iterator 接口之上实现,但由于它很少使用,因此将它包含在每个人都必须实现的接口中是没有意义的。

10.Iterator 和 ListIterator 有什么区别?

我们可以使用 Iterator 遍历 Set 和 List 集合,而 ListIterator 只能与 List 一起使用。Iterator 只能向前遍历,而 ListIterator 可用于双向遍历。ListIterator 继承自 Iterator 接口,并带有额外的功能,例如添加元素、替换元素、获取上一个和下一个元素的索引位置。

11.迭代列表有哪些不同的方法?

我们可以用两种不同的方式迭代一个列表——使用迭代器和使用 for-each 循环。
<font style="vertical-align: inherit;"><font style="vertical-align: inherit;">      列表<String> strList = new ArrayList<>();</font></font><font></font><font style="vertical-align: inherit;"><font style="vertical-align: inherit;">
        //使用for-each循环</font></font><font></font><font style="vertical-align: inherit;"><font style="vertical-align: inherit;">
        对于(字符串对象:strList){</font></font><font></font><font style="vertical-align: inherit;"><font style="vertical-align: inherit;">
            System.out.println(obj);</font></font><font></font><font style="vertical-align: inherit;"><font style="vertical-align: inherit;">
        }</font></font><font></font><font style="vertical-align: inherit;"><font style="vertical-align: inherit;">
        //使用迭代器</font></font><font></font><font style="vertical-align: inherit;"><font style="vertical-align: inherit;">
        迭代器<String> it = strList.iterator();</font></font><font></font><font style="vertical-align: inherit;"><font style="vertical-align: inherit;">
        而(it.hasNext()){</font></font><font></font><font style="vertical-align: inherit;"><font style="vertical-align: inherit;">
            字符串 obj = it.next();</font></font><font></font><font style="vertical-align: inherit;"><font style="vertical-align: inherit;">
            System.out.println(obj);</font></font><font></font><font style="vertical-align: inherit;"><font style="vertical-align: inherit;">
        }</font></font>

使用迭代器更加线程安全,因为它确保如果底层列表元素被修改,它将抛出
ConcurrentModificationException.

12.您对迭代器快速失败属性有何理解?

每次我们尝试获取下一个元素时,迭代器快速失败的属性都会检查底层集合结构中的任何修改。如果发现任何修改,它会抛出
ConcurrentModificationException. 除了 ConcurrentHashMap 和 CopyOnWriteArrayList 等并发集合类之外,Collection 类中的所有 Iterator 实现都是设计为快速失败的。

13.快速故障和故障安全之间有什么区别?

迭代器故障安全属性与基础集合的克隆一起使用,因此它不受集合中任何修改的影响。按照设计,java.util包中的所有集合类都是快速故障的,而其中的集合类java.util.concurrent是故障安全的。快速失败的迭代器抛出
ConcurrentModificationException,而故障安全迭代器从不抛出 ConcurrentModificationException。检查此帖子以获取CopyOnWriteArrayList 示例。

14.迭代集合时如何避免
ConcurrentModificationException?


ConcurrentModificationException我们可以在迭代集合时使用并发集合类来避免,例如 CopyOnWriteArrayList 而不是 ArrayList。
检查这篇文章的ConcurrentHashMap 示例。

15.为什么没有 Iterator 接口的具体实现?

Iterator 接口声明了用于迭代集合的方法,但它的实现由 Collection 实现类负责。每个返回迭代器进行遍历的集合类都有自己的迭代器实现嵌套类。
这允许集合类选择迭代器是快速故障还是故障安全。例如 ArrayList 迭代器是故障快速的,而 CopyOnWriteArrayList 迭代器是故障安全的。

16.什么是
UnsupportedOperationException?


UnsupportedOperationException是用于指示不支持该操作的异常。它在 JDK 类中广泛使用,在集合框架中为所有操作
java.util.Collections.UnmodifiableCollection抛出此异常。addremove

17.HashMap 在 Java 中是如何工作的?

Map.EntryHashMap 在静态嵌套类实现中存储键值对。HashMap 做散列算法,在putandget方法中使用hashCode()和equals()方法。当我们通过传递键值对调用put方法时,HashMap使用Key hashCode()和散列来找出索引来存储键值对. 条目存储在 LinkedList 中,因此如果已经存在条目,则使用 equals() 方法检查传递的键是否已存在,如果存在则覆盖值,否则创建新条目并存储此键值条目.当我们通过传递Key调用get方法时,它再次使用hashCode()来查找数组中的索引,然后使用equals()方法找到正确的Entry并返回它的值。下图将清楚地解释这些细节。

关于 HashMap 需要了解的其他重要事项是容量、负载因子、阈值调整大小。HashMap 初始默认容量为 32,加载因子为 0.75。阈值是容量乘以负载因子,每当我们尝试添加条目时,如果映射大小大于阈值,HashMap 会将映射的内容重新散列到具有更大容量的新数组中。容量始终是 2 的幂,因此如果您知道需要存储大量键值对,例如在缓存数据库中的数据时,最好使用正确的容量和负载因子初始化 HashMap。

18.hashCode() 和 equals() 方法的重要性是什么?

HashMap 使用 Key 对象 hashCode() 和 equals() 方法来确定放置键值对的索引。当我们尝试从 HashMap 中获取值时,也会使用这些方法。如果这些方法没有正确实现,两个不同的 Key 可能会产生相同的 hashCode() 和 equals() 输出,在这种情况下,HashMap 不会将其存储在不同的位置,而是将它们视为相同并覆盖它们。类似地,所有不存在的集合类'不存储重复数据使用 hashCode() 和 equals() 来查找重复数据,因此正确实现它们非常重要。equals() 和 hashCode() 的实现应该遵循这些规则。

如果o1.equals(o2),那么o1.hashCode() == o2.hashCode()应该总是true。

如果o1.hashCode() == o2.hashCode是真的,那并不意味着o1.equals(o2)会是true。

19.我们可以使用任何类作为 Map 键吗?我们可以使用任何类作为 Map Key,但是在使用它们之前应该考虑以下几点。

如果类重写了 equals() 方法,它也应该重写 hashCode() 方法。

对于所有实例,该类应遵循与 equals() 和 hashCode() 相关的规则。有关这些规则,请参阅前面的问题。

如果在 equals() 中未使用类字段,则不应在 hashCode() 方法中使用它。

用户定义键类的最佳实践是使其不可变,以便可以缓存 hashCode() 值以获得快速性能。不可变类也确保 hashCode() 和 equals() 将来不会改变,这将解决可变性的任何问题。
例如,假设我有一个MyKey用于 HashMap 键的类。

//MyKey name argument passed is used for equals() and hashCode()<font></font>
MyKey key = new MyKey('Pankaj'); //assume hashCode=1234<font></font>
myHashMap.put(key, 'Value');<font></font>
<font></font>
// Below code will change the key hashCode() and equals()<font></font>
// but it's location is not changed.<font></font>
key.setName('Amit'); //assume new hashCode=7890<font></font>
<font></font>
//below will return null, because HashMap will try to look for key<font></font>
//in the same index as it was stored but since key is mutated, <font></font>
//there will be no match and it will return null.<font></font>
myHashMap.get(new MyKey('Pankaj'));

这就是为什么 String 和 Integer 主要用作 HashMap 键的原因。

20.Map 接口提供了哪些不同的 Collection 视图?

Map 接口提供了三个集合视图:

Set keySet():返回此映射中包含的键的 Set 视图。集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。如果在对集合进行迭代时修改了映射(通过迭代器自己的删除操作除外),则迭代的结果是不确定的。该集合支持元素移除,即通过 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作从映射中移除相应的映射。它不支持 add 或 addAll 操作。

集合值():返回此映射中包含的值的集合视图。集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。如果在对集合进行迭代时修改了映射(通过迭代器自己的删除操作除外),则迭代的结果是不确定的。该集合支持元素移除,即通过 Iterator.remove、Collection.remove、removeAll、retainAll 和 clear 操作从映射中移除相应的映射。它不支持 add 或 addAll 操作。

Set<Map.Entry<K, V>> entrySet():返回此映射中包含的映射的 Set 视图。集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。如果在对集合进行迭代时修改了映射(通过迭代器自己的删除操作或通过迭代器返回的映射条目上的 setValue 操作除外),则迭代的结果是未定义的。该集合支持元素移除,即通过 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作从映射中移除相应的映射。它不支持 add 或 addAll 操作。

总结:
“不积跬步,无以至千里”,希望未来的你能成为:有梦为马 随处可栖!加油,为大家收集了更多最新的面试资料,有文档、有攻略、有视频。有需要的同学MM




#面试##春招##实习##笔试题目##面经##求面经##Java##MySQL#
全部评论
能全部答上来的,应该都是大神
点赞 回复 分享
发布于 2022-05-19 21:19
这个面试题有把我难到啊,特别是前面几道😂
点赞 回复 分享
发布于 2022-07-14 12:36

相关推荐

昨天 16:18
牛客_运营
点赞 评论 收藏
分享
评论
1
10
分享
牛客网
牛客企业服务