Java 集合面试题及答案

16 Mar 2025 | 18 分钟阅读

在 Java 中,集合面试题是面试官最常问的。以下是关于集合最常被问到的面试题及答案。


1) 什么是 Java 中的 Collection 框架?

Collection 框架是用于以对象形式存储和操作数据的类和接口的组合。它为此目的提供了各种类,如 ArrayList、Vector、Stack 和 HashSet 等,以及 List、Queue、Set 等接口。这个框架通过提供一种标准化的处理项集合的方法,提供了一种高效灵活的数据结构处理方式。


2) 数组和集合之间的主要区别是什么?

在存储对象引用和操作数据方面,数组和集合在某些方面是相似的,但它们在很多方面有所不同。

数组和 Collection 之间的主要区别定义如下:

方面Array集合
大小固定大小动态(可调整大小)
元素类型同质(同类型)异质(不同类型)
方法可用方法有限丰富的方法用于操作和迭代
灵活性灵活性有限操作和扩展的高度灵活性
用途基本数据存储高级数据操作和存储

3) 解释 Collection 框架中使用的各种接口?

Collection 框架实现了各种接口,Collection 接口和 Map 接口 (java.util.Map) 是 Java Collection 框架中最常用的接口。下面是 Collection 框架接口的列表:

1. Collection 接口

这是集合层次结构的基础接口。它的元素是它所表示的项的集合。集合中可能包含重复的元素,并且元素的顺序可能会改变。ArrayList、LinkedList 和 HashSet 等常用类是此接口的实现示例。

语法

其中 <E> 表示该接口是泛型类型

2. List 接口

List 扩展了 Collection 接口,表示一个有序的元素集合。它允许重复元素,并提供按索引访问元素的方法。List 接口的实现包括 ArrayList、LinkedList 和 Vector。

语法

3. Set 接口

Set 接口扩展了 Collection 接口,表示一个不能包含重复元素的集合。它不保证元素的顺序,而是模拟数学集合的抽象。HashSet、TreeSet 和 LinkedHashSet 实现 Set 接口。

语法

4. Queue 接口

Queue 是一个用于存储待处理项的集合,它扩展了 Collection 接口。它的元素按 FIFO(先进先出)顺序排列。Queue 接口由 LinkedList、PriorityQueue 和 ArrayDeque 实现。

语法

5. Deque 接口

Deque 代表“双端队列”,并扩展了 Queue 接口。它表示一个支持在两端插入和删除元素的线性集合。Deque 可以用作标准队列(FIFO)和栈(LIFO)。Deque 接口的实现包括 LinkedList 和 ArrayDeque。

语法

5. Map 接口

与其他的 Collection 接口不同,Map 接口不扩展 Collection 接口。它表示键值对的映射,其中每个键都关联一个值。HashMap、TreeMap 和 LinkedHashMap 是 Map 实现的示例。此外,SortedMap 接口扩展了 Map,并提供了一个按升序保留键的 Map。

语法


4) ArrayList 和 Vector 有什么区别?

方面ArrayListVector
同步ArrayList 不同步。Vector 同步。
遗产ArrayList 不是遗留类。Vector 是遗留类。
扩容策略ArrayList 的大小会增加数组大小的 50%。Vector 的大小会加倍增加数组大小。
线程安全ArrayList 不是“线程安全”的,因为它不同步。Vector 列表是“线程安全”的,因为它所有的每个方法都是同步的。
性能由于缺乏同步开销,通常速度更快。由于同步开销,速度较慢。
Iterator快速失败迭代器(如果迭代期间修改,则抛出 ConcurrentModificationException)。安全迭代器(如果迭代期间修改,则不抛出 ConcurrentModificationException)。
用途在非并发环境中首选。用于遗留或并发环境。

5) ArrayList 和 LinkedList 有什么区别?

方面ArrayListLinkedList
数据结构ArrayList 使用动态数组。LinkedList 使用双向链表。
效率ArrayList 进行操作的效率不高,因为需要进行大量操作。LinkedList 进行操作很高效。
数据存储ArrayList 更适合存储和获取数据。LinkedList 更适合操作数据。
访问ArrayList 提供随机访问。LinkedList 不提供随机访问。
内存开销ArrayList 内存开销较小,因为它只存储对象。LinkedList 内存开销较大,因为它存储对象以及该对象的地址。

6) Iterator 和 ListIterator 有什么区别?

Iterator 只能向前遍历元素,而 ListIterator 可以向前和向后遍历元素。

方面IteratorListIterator
遍历方向Iterator 只能向前遍历元素。ListIterator 可以向前和向后遍历元素。
适用的集合Iterator 可用于 List、Set 和 Queue。ListIterator 只能用于 List。
支持的操作Iterator 在遍历集合时只能执行 remove 操作。ListIterator 在遍历集合时可以执行 add、remove 和 set 操作。
当前位置管理它不维护当前位置指针。它维护当前位置指针。
集合修改它不能在遍历期间添加或修改元素。它可以在遍历期间添加、删除或修改元素。
实现类java.util.Iteratorjava.util.ListIterator

7) Iterator 和 Enumeration 有什么区别?

方面Iterator枚举
遍历能力Iterator 可以遍历遗留和非遗留元素。Enumeration 只能遍历遗留元素。
快速失败行为Iterator 是快速失败的。Enumeration 不是快速失败的。
速度Iterator 比 Enumeration 慢。Enumeration 比 Iterator 快。
集合修改Iterator 在遍历集合时可以执行 remove 操作。Enumeration 只能对集合执行遍历操作。
当前位置管理它维护一个当前位置指针。它不维护当前位置指针。
集合类型支持List、Set、Queue、Map主要支持 Vector、Hashtable 和 Properties 等遗留集合类。
并发修改检测并发修改。没有内置的修改检测机制。
方法它有 hasNext()、next() 等附加方法。它有 hasMoreElements()、nextElement() 等方法。

8) List 和 Set 有什么区别?

比较点列表Set
元素重复它可以包含重复元素。它只包含唯一项。
元素顺序它维护插入顺序。它不保留插入顺序。
遗留类它包含遗留类 Vector。它没有任何遗留类。
空值它允许多个 null 值。它只允许一个 null 值。
子接口它包括 ListIterator、ListIterator。它包括 SortedSet、NavigableSet。
示例类ArrayList、LinkedList、Vector。HashSet、TreeSet。

9) HashSet 和 TreeSet 有什么区别?

方面HashSetTreeSet
元素顺序不维护特定顺序按升序维护元素
实现结构使用哈希表实现使用树结构实现
性能通常速度更快,因为是常数时间操作由于 log(n) 时间操作,速度稍慢
底层数据结构由 HashMap 支持由 TreeMap 支持
元素顺序无序有序

10) Set 和 Map 有什么区别?

方面SetMap
要素它只包含值。它包含键值对。
元素唯一性它包含唯一值。它可以包含唯一键和重复值。
Null 值处理它可以容纳一个 null 值。它可以有一个 null 键和多个 null 值。
访问元素它通过迭代或特定方法访问。它通过键访问。
示例HashSet、TreeSet、LinkedHashSetHashMap、TreeMap、LinkedHashMap

11) HashSet 和 HashMap 有什么区别?

方面HashSetHashMap
要素它只包含值。它包含键值对。
接口实现它实现 Set 接口。它实现 Map 接口。
迭代它可以直接迭代需要转换为 Set 才能迭代
元素唯一性它不能有重复值。它可以包含具有唯一键的重复值。
Null 值处理它可以容纳一个 null 值。它可以有一个 null 键和多个 null 值。
内部数据结构它使用哈希表实现。它使用桶数组(哈希表和链表)实现。

12) HashMap 和 TreeMap 有什么区别?

方面HashMapTreeMap
元素顺序没有特定顺序。它按升序维护元素。
实现结构它使用哈希表实现。它使用树结构实现。
排序能力它可以按键或值排序。它只能按键排序。
Null 键处理它可以有一个 null 键和多个 null 值。它不能有 null 键,但可以有多个 null 值。
内部数据结构它使用哈希表进行存储和检索。它使用红黑树进行存储和检索。

13) HashMap 和 Hashtable 有什么区别?

方面HashMapHashtable
同步它不同步。它同步。
Null 键和值处理它可以有一个 null 键和多个 null 值。它不能有 null 键或值。
线程安全它不是线程安全的。它是线程安全的。
继承它继承了 AbstractMap 类。它继承了 Dictionary 类。
性能它通常提供更好的性能。由于同步,它可能有性能开销。
迭代行为迭代顺序可能不可预测迭代按插入顺序进行
迭代器故障安全性它是快速失败的迭代器。它是安全失败的迭代器。

14) Collection 和 Collections 有什么区别?

方面集合集合
类型它是一个接口。它是一个类。
目的它为 List、Set 和 Queue 等数据结构定义了标准功能。它提供了用于排序和同步集合的实用方法。
方法它定义了数据结构操作的方法。它为各种集合操作提供了静态方法。
示例实现ArrayList、LinkedList、HashSetCollections.sort()、Collections.synchronizedList()
Null 检查它可以包含 null 元素。它对 null 性没有直接影响。
可扩展性它可以被其他接口和类实现。它不能被扩展或实现。

15) Comparable 和 Comparator 有什么区别?

方面ComparableComparator
排序序列它提供一个排序序列。它提供多个排序序列。
方法它需要实现 compareTo() 方法。它需要实现 compare() 方法。
地点它属于 java.lang 包。它属于 java.util 包。
类更改它修改了实际类。它不修改实际类。
灵活性它灵活性较低,因为它与对象的类相关联。它更灵活,允许根据不同标准进行排序。

16) BlockingQueue 是什么意思?

BlockingQueue 是一个扩展了 Queue 接口的接口。它在检索、插入和删除等操作中提供了并发性。在检索元素时,它会等待队列非空。在存储元素时,它会等待可用空间。BlockingQueue 不能包含 null 元素,并且其实现是线程安全的。

需要记住的是,BlockingQueue 实现不允许包含 null 元素。这些实现也是线程安全的,这使得它们适用于多个线程必须同时访问和修改共享队列的情况。在并发编程中,BlockingQueue 通常用于以线程安全的方式管理生产者和消费者线程之间的数据交换。

语法


17) Properties 文件的优点是什么?

如果我们更改 Properties 文件中的值,则无需重新编译 Java 类。因此,这使得应用程序易于管理。它用于存储需要频繁更改的信息。请看以下示例。

Test.java

输出

Tpointtech
12345

说明

提供的代码片段使用 Properties 类从 db.properties 文件加载指示配置设置的键值对。然后使用 getProperty() 方法从 Properties 文件中检索与特定键(“user”和“password”)关联的值,并将它们显示在控制台上。通过将配置数据与应用程序逻辑分离,这种方法可以提高灵活性和可管理性。

db.properties


18) hashCode() 方法有什么作用?

Java 的 hashCode() 函数生成对象的哈希码值。包括 HashMap、HashSet 等在内的许多数据结构使用此值来高效地组织和存储对象。hashCode() 方法的主要目标是为对象的状态提供一个唯一的整数表示。

  1. 即使 hashCode() 方法试图为每个对象生成唯一的哈希码,不同的对象也可以共享相同的哈希码。
  2. 即使它们的 equals() 方法表明它们相等,两个对象也可能不总是生成相同的哈希码。为了保持一致性,如果 equals() 方法确定两个对象相等,它们应该共享相同的哈希码。
  3. equals() 函数和 hashCode() 方法通常一起使用。为了保持一致性,建议覆盖 equals() 方法的类也覆盖 hashCode() 函数。这可确保相同类型的对象具有相同的哈希码,这对于某些数据结构有效运行是必需的。
  4. Java 的 Object 类提供了 hashCode() 方法的默认实现,该方法根据对象内存位置生成哈希码。为了提供基于对象状态更有意义的哈希码,用户定义的类通常会覆盖此函数。

19) 为什么要覆盖 equals() 方法?

equals 方法用于检查两个对象是否相同。如果我们想根据属性来检查对象,则需要覆盖它。

例如,Employee 是一个具有 3 个数据成员:id、name 和 salary 的类。但是,我们想通过 salary 来检查 Employee 对象的相等性。然后,我们需要覆盖 equals() 方法。


20) 如何同步 List、Set 和 Map 元素?

是的,Collections 类提供了使 List、Set 或 Map 元素同步的方法。

这些方法通过将提供的集合(List、Set 或 Map)封装在同步包装器中,提供线程安全的集合操作。这可以避免危险的竞态条件并确保数据完整性,尤其是在多个线程可能并发访问或修改集合的情况下。


21) 泛型集合的优点是什么?

与非泛型集合相比,Java 中的泛型集合提供了许多优点。

1. 消除类型转换

使用泛型集合的一个主要优点是不需要显式类型转换。从非泛型集合中检索元素时,我们必须将其转换为正确的数据类型;如果转换不正确,可能会导致运行时问题。通过使用泛型,由于编译器在编译时保证了类型安全,我们可以直接使用正确的数据类型而无需进行转换。

2. 类型安全

通过使用泛型集合,编译器可以确保集合内仅使用正确的数据类型,从而提供类型安全性。通过这样做,可以避免因数据类型不匹配而导致的运行时问题。在构建泛型集合时,通过使用类型参数明确定义集合可以包含的组件类型,我们可以减少因错误数据类型导致的错误的可能性。

3. 编译时检查

编译时检查是泛型集合的另一个优点。由于类型信息在编译时可用,编译器可以在代码执行之前发现与类型相关的检查和问题。通过及早发现开发过程中的任何缺陷,这可以提高代码的稳定性和可靠性。开发人员可以通过主动解决编译期间发现的错误来降低运行时错误的风险并增强其代码。


22) 哈希表中什么是哈希冲突,Java 中如何处理?

具有相同哈希值的两个不同键称为哈希冲突。为避免此问题,将在单个哈希桶中保留两个单独的条目。

有两种方法可以避免哈希冲突:

1. 分离链接

根据此方法,哈希表中的每个桶都有一个链表,其中包含所有哈希到同一索引的键值对,或者它可能包含另一个数据结构,如树。当发生冲突时,新的键值对会被添加到与该桶关联的链表中。此技术可以使哈希表操作保持相当快的速度,同时允许具有相同哈希值的多个条目共存于同一个桶中。但是,由于需要存储链表,可能会产生额外的内存开销,如果链表变得太大,性能可能会受到影响。

2. 开放寻址

当开放寻址发生冲突时,哈希表会寻找另一个位置来存储冲突的键值对。开放寻址可以通过多种方法实现,包括双重散列、二次探测和线性探测。例如,线性探测涉及按顺序扫描哈希表中的槽,直到找到一个空槽,然后可以将冲突的元素插入其中。

尽管开放寻址消除了存储链表等额外数据结构的需要,但在负载因子较高的情况下,它可能会导致性能下降和聚集。

此外,组件的分布和哈希表操作的有效性可能会受到探测技术选择的影响。


23) Dictionary 类是什么?

Java 的 Dictionary 类通过提供 put()、get()、remove() 和 elements() 等方法,可以更轻松地存储键值对。但是,由于它是一个抽象类,因此需要通过子类化来实现。由于其更优的功能和效率,Hashtable 在当前 Java 中已被 Dictionary 大部分取代。

  • put(key, value): 该方法将给定的值与字典中的给定键关联起来。
  • get(key): 返回字典中与给定键对应的键。
  • remove(key): 清空字典中的键及其对应的值。
  • elements(): 提供字典中所有值的列表。

24) 基于哈希的集合的默认负载因子大小是多少?

默认的负载因子大小为 **0.75**。默认容量的计算方法是初始容量 * 负载因子。例如,16 * 0.75 = 12。因此,12 是 Map 的默认容量。


25) Fail-fast 是什么意思?

Java 中的“Fail-fast”(快速失败)是指迭代器或集合类。在迭代期间,结构性更改可能涉及元素的添加、删除或修改。与存在不可预测行为或数据损坏风险的其他方法不同,快速失败行为可确保迭代器及时且一致地失败。

快速失败迭代器通过在发现并发修改时提供即时反馈来保证集合的完整性,而无需额外的内存开销。


26) 数组和 ArrayList 有什么区别?

数组和 ArrayList 之间的主要区别如下。

方面ArrayArrayList
大小灵活性数组大小固定。ArrayList 大小是动态的。
类型它是静态类型。它是动态类型。
数据类型它可以存储基本类型和对象。它只能存储对象,不能存储原始类型。
声明语法它使用方括号(例如,int[] arr)。它使用 <> 进行声明(例如,ArrayListlist)。
框架归属它不是 Java 集合框架的一部分。它是 Java 集合框架的一部分。
访问方法使用索引直接访问。通过 get() 和 set() 等方法访问。
多维度它支持多维数组。它本质上是一维的。

27) 数组的 length 和 ArrayList 的 size 有什么区别?

数组的长度可以使用 length 属性获取,而 ArrayList 不支持 length 属性,但我们可以使用 size() 方法来获取列表中对象的数量。

获取数组长度

获取 ArrayList 的大小


28) 如何将 ArrayList 转换为 Array,将 Array 转换为 ArrayList?

我们可以使用 Arrays 类的 asList() 方法将 Array 转换为 ArrayList。asList() 方法是 Arrays 类的静态方法,它接受 List 对象。请参考以下语法:

我们可以使用 ArrayList 类的 toArray() 方法将 ArrayList 转换为 Array。请参考以下语法将 ArrayList 转换为 List 对象。


29) 如何使 Java ArrayList 只读?

我们可以通过调用 Collections.unmodifiableCollection() 方法来获取一个 Java ArrayList,使其变为只读。当我们定义一个 ArrayList 为只读时,我们不能通过 add()、remove() 或 set() 方法修改集合。

ReadOnlyArrayList.java

输出

Cannot add elements to the read-only list.
Read-only list: [Apple, Banana, Orange, Grape]

30) 如何从 ArrayList 中删除重复项?

有两种方法可以从 ArrayList 中删除重复项。

  • 使用 HashSet:通过使用 HashSet,我们可以从 ArrayList 中删除重复的元素,但它不会保留插入顺序。
  • 使用 LinkedHashSet:我们也可以使用 LinkedHashSet 而不是 HashSet 来维护插入顺序。

使用 LinkedHashSet 从 ArrayList 中删除重复元素的流程

  • 将 ArrayList 的所有元素复制到 LinkedHashSet。
  • 使用 clear() 方法清空 ArrayList,这将删除列表中的所有元素。
  • 现在将 LinkedHashSet 的所有元素复制到 ArrayList。

31) 如何反转 ArrayList?

要反转 ArrayList,我们可以使用 Collections 类的 reverse() 方法。请参考以下示例。

ReverseArrayList.java

输出

printing the list....
10
50
30
printing list in reverse order....
30
50
10

32) 如何按降序对 ArrayList 进行排序?

要按降序对 ArrayList 进行排序,我们可以使用 Collections 类的 reverseOrder() 方法。请参考以下示例。

ReverseArrayList.java

输出

printing the list....
10
50
30
60
20
90
printing list in descending order....
90
60
50
30
20
10

33) 如何同步 ArrayList?

我们可以通过两种方式同步 ArrayList。

1. 使用 Collections.synchronizedList() 方法

使用此方法,Collections 类的 synchronizedList() 方法将包装您的 ArrayList。它通过确保一次只有一个线程可以访问列表来使列表线程安全。

2. 使用 CopyOnWriteArrayList<T>

这是 Java 内置的、支持同步的 List 接口的自定义版本。每次执行修改底层数组的操作时,都会创建一个新副本。这消除了显式同步的需要,并允许对列表进行安全的并发访问。


34) 何时使用 ArrayList 和 LinkedList?

LinkedList 更适合更新操作,而 ArrayList 更适合搜索操作。虽然 ArrayList 和 LinkedList 确实具有不同的性能特征,使得它们更适合某些操作,但何时使用一个而不是另一个的决定应考虑除了更新和搜索操作之外的各种因素。

ArrayList

  • 当您需要按索引快速访问元素时,请使用 ArrayList,因为它提供了基于位置的元素的恒定时间访问。
  • ArrayList 对于读密集型场景很有效,在这些场景中,元素经常被访问但很少被修改。
  • 当内存开销是问题时,它也适用,因为 ArrayList 的内存占用比 LinkedList 小,原因在于其简单的基于数组的结构。

LinkedList

  • 当您需要在列表的任意位置进行频繁的插入或删除时,请使用 LinkedList,因为它提供了恒定的插入和删除操作时间。
  • LinkedList 在频繁修改方面的性能优于 ArrayList,尤其是在插入或删除列表中间的元素时,因为它不需要移动元素。
  • 它适用于您需要遍历整个列表并执行修改的场景,因为与 ArrayList 的迭代器相比,LinkedList 的迭代器在这种情况下性能更好。

1 2 3 4 5 6 7 8