大家好,我是栗筝i,从 2022 年 10 月份开始,我持续梳理出了全面的 Java 技术栈内容,一方面是对自己学习内容进行整合梳理,另一方面是希望对大家有所帮助,使我们一同进步。得到了很多读者的正面反馈。
而在 2023 年 10 月份开始,我将推出 Java 面试题/知识点系列内容,期望对大家有所助益,让我们一起提升。
本篇是对 Java 基础系列的面试题 / 知识点的总结的上篇
- 全网最全的 Java 面试题内容梳理(持续更新中)
- Java基础面试题&知识点总结(上篇)
- Java基础面试题&知识点总结(下篇)
文章目录
- 1、Java基础面试题问题
- 2、Java基础面试题解答
- 2.1、Java集合架构相关
- 2.2、JavaList集合相关
- 2.3、JavaQueue集合相关
- 2.4、JavaSet集合相关
1、Java基础面试题问题
- 问题 1. 简述 Java 集合类都有哪些?
- 问题 2. 简述 Collection 与 Collections 的区别
- 问题 3. 简述 List、Set、Map 三者的区别
- 问题 4. 介绍一下 ArrayList 的底层结构和相关原理
- 问题 5. 介绍一下 ArrayList 的扩容机制
- 问题 6. 介绍一下 ArrayList 删除元素时有哪些顾虑?
- 问题 7. 介绍一下 ArrayList 中怎么在遍历移除一个元素?
- 问题 8. 介绍一下 ArrayList 是线程安全的吗?如何保证 ArrayList 的线程安全?
- 问题 9. 简述 ArrayList 和 LinkedList 的区别
- 问题 10. 简述 ArrayList 和 Vector 的区别
- 问题 11. 简述 ArrayList 与 Array 的区别
- 问题 12. 介绍一下 LinkedList 的底层结构和相关原理
- 问题 13. 介绍一下 Vector 的底层结构和相关原理
- 问题 14. 介绍一下 Stack 的底层结构和相关原理
- 问题 15. 请解释一下 Java 中的 CopyOnWriteArrayList
- 问题 16. 简述队列和栈是什么,它们有何区别?
- 问题 17. 请解释一下 Java 中的 Queue 和 Deque?
- 问题 18. 请解释一下 Java 中的 PriorityQueue?
- 问题 19. 请解释一下 Java 中的 BlockingQueue?
- 问题 20. 介绍一下 HashSet 的底层结构和相关原理
- 问题 21. 介绍一下 LinkedHashSet 的底层结构和相关原理
- 问题 22. 介绍一下 TreeSet 的底层结构和相关原理
- 问题 23. 请解释一下 Java 中的 EnumSet
- 问题 24. 请解释一下 Java 中的 SortedSet
- 问题 25. 请解释一下 Java 中的 NavigableSet
2、Java基础面试题解答
2.1、Java集合架构相关
- 问题 1. 简述 Java 集合类都有哪些?
解答:Java 集合类呢主要是指 java.Util包 下的集合容器。主要包含三种:List、Set、Map,其中 List、Set 主要继承自 Collection 接口,然后它们三个又都依赖了 Iterator 迭代器;
- 首先,List 常用的就是 ArrayList、LinkedList、Vector,其中 ArrayList 的底层实现是数组,LinkedList 的实现是双向链表,此外 LinkedList 还实现了 Queue 队列(也在 Collection 下的接口),Vector 就是 ArrayList 的线程安全版本,但不推荐使用,此外 Java 中的栈 Stack 还继承自 Vector;
- 其次,Set 集合常用的就是 HashSet 和 TreeSet,它们的实现就是依赖于 HahsMap 和 TreeMap;
- 最后,Map 集合就是 HahsMap 和 TreeMap了。这些实现大多数都是非线程安全的。
- 问题 2. 简述 Collection 与 Collections 的区别
解答:Collection 和 Collections 在 Java 中是两个不同的概念。
-
Collection是一个接口,它是 List、Set 和 Queue 接口的父接口,定义了适用于任何集合的操作,如 add、remove 和 contains。 -
Collections是一个类,它包含了一些静态的工具方法,这些方法可以对集合进行操作,如排序(sort)、查找(binarySearch)、修改(fill、copy)、同步控制(synchronizedXxx)等。
- 问题 3. 简述 List、Set、Map 三者的区别
解答:List、Set 和 Map 是 Java 集合框架中的三种基本接口,它们的区别主要体现在存储内容和使用方式上。
-
List:是一个有序的集合,可以包含重复的元素。它提供了索引的访问方式,我们可以通过索引(列表的位置)来访问或者搜索列表中的元素。主要实现类有 ArrayList、LinkedList 和 Vector。 -
Set:是一个不允许有重复元素的集合,也就是说,每个元素只能出现一次。它不提供索引访问方式,主要用于存在性检查,即检查一个元素是否存在。主要实现类有 HashSet、LinkedHashSet 和 TreeSet。 -
Map:是一个键值对的集合,每个键映射到一个值,键不能重复,每个键最多只能映射到一个值。它提供了基于键的访问方式,我们可以通过键来获取、删除或者检查值。主要实现类有 HashMap、LinkedHashMap、TreeMap 和 Hashtable。
2.2、JavaList集合相关
- 问题 4. 介绍一下 ArrayList 的底层结构和相关原理
解答:ArrayList 是 Java 中常用的一种动态数组实现,其底层是基于数组实现的。
-
存储结构:
ArrayList内部使用一个数组(elementData)来存储元素。当添加元素时,如果数组已满,就会创建一个新的更大的数组,并将原数组的内容复制到新数组中,这个过程称为扩容。 -
扩容机制:
ArrayList的扩容机制是,每次扩容时,新数组的大小是原数组大小的 1.5 倍。如果这个值仍然不足以满足需求,那么新数组的大小就直接设置为需求的大小。 -
插入和删除:
ArrayList在尾部插入和删除元素非常高效,时间复杂度为 O(1)。但是在中间或头部插入和删除元素需要移动大量元素,时间复杂度为 O(n)。 -
访问元素:由于底层是数组,所以
ArrayList支持随机访问,按索引访问元素的时间复杂度为 O(1)。 -
线程安全:
ArrayList是非线程安全的,如果需要在多线程环境下使用,可以使用Collections.synchronizedList()方法返回一个线程安全的ArrayList,或者使用线程安全的Vector。
总的来说,ArrayList 是一种动态数组结构,适合随机访问场景,但在中间或头部插入和删除元素时效率较低。
- 问题 5. 介绍一下 ArrayList 的扩容机制
解答:ArrayList 的扩容机制是这样的:
-
当我们向
ArrayList添加元素时,如果当前数组已满(即数组的大小等于其元素的数量),那么ArrayList就需要进行扩容。 -
ArrayList的扩容过程是创建一个新的数组,这个新数组的大小是原数组大小的 1.5 倍(即原数组大小加上原数组大小的一半)。具体来说,如果原数组大小为 10,那么新数组的大小就是 15。 -
创建新数组后,
ArrayList会将原数组中的所有元素复制到新数组中,然后丢弃原数组。 -
这个扩容过程是自动进行的,我们在使用
ArrayList时无需关心其扩容机制。
需要注意的是,ArrayList 的这种扩容机制意味着其在添加大量元素时可能会有一定的性能开销,因为每次扩容都需要创建新数组并复制元素。如果我们预先知道 ArrayList 将要存储的元素数量,可以在创建 ArrayList 时指定其初始大小,这样可以减少扩容操作,提高性能。
- 问题 6. 介绍一下 ArrayList 删除元素时有哪些顾虑?
解答:在使用 ArrayList 删除元素时,需要注意以下几点:
-
ConcurrentModificationException:在遍历ArrayList的过程中直接调用ArrayList的remove()方法删除元素,可能会抛出ConcurrentModificationException异常。正确的做法是使用Iterator的remove()方法删除元素。 -
性能考虑:
ArrayList在删除元素时,为了保持元素的连续性,会将后面的元素向前移动,所以删除元素的性能与ArrayList的大小成正比。如果需要频繁删除元素,可以考虑使用LinkedList。 -
索引越界:在调用
remove(int index)方法时,如果索引超出了ArrayList的范围,会抛出IndexOutOfBoundsException异常。所以在删除元素前,需要确保索引是有效的。 -
自动装箱:
ArrayList的remove()方法有两个重载版本:remove(int index)和remove(Object o)。如果ArrayList存储的是基本类型的包装类,比如Integer,那么在调用remove()方法时需要注意自动装箱可能带来的问题。例如,list.remove(1)可能不是删除元素1,而是删除索引为1的元素。 -
空指针:如果
ArrayList中存储的是对象,那么在删除元素时,如果ArrayList中存在null,需要注意NullPointerException异常。
- 问题 7. 介绍一下 ArrayList 中怎么在遍历移除一个元素?
解答:在遍历 ArrayList 时移除元素,需要注意 ConcurrentModificationException 异常。以下是几种常见的移除元素的方法:
- 使用
Iterator的remove()方法。这是最安全且推荐的方式。
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {String item = iterator.next();if (需要移除的条件) {iterator.remove();}
}
- 使用
List的remove()方法,但需要倒序遍历。
for (int i = list.size() - 1; i >= 0; i--) {if (需要移除的条件) {list.remove(i);}
}
- 使用 Java 8 的
Collection.removeIf()方法。
list.removeIf(item -> 需要移除的条件);
以上三种方法都可以在遍历 ArrayList 时移除元素,但是推荐使用 Iterator 的 remove() 方法,因为它在面对并发修改时可以提供正确的行为。
- 问题 8. 介绍一下 ArrayList 是线程安全的吗?如何保证 ArrayList 的线程安全?
解答:ArrayList 是非线程安全的,它的方法没有进行同步处理,所以在多线程环境下可能会出现问题。
如果需要在多线程环境下使用 ArrayList,有以下几种方式可以保证线程安全:
- 使用
Collections.synchronizedList()方法创建一个同步的ArrayList:
List<String> list = Collections.synchronizedList(new ArrayList<>());
这个方法会返回一个线程安全的 List,所有的方法都进行了同步处理。
- 使用
CopyOnWriteArrayList,这是一个线程安全的List实现:
List<String> list = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList 在每次修改操作(如添加、删除元素)时,都会复制一份新的数组,这样可以避免修改操作对遍历操作的影响,提供了一种读写分离的机制。
-
使用并发包
java.util.concurrent中的其他线程安全集合,如ConcurrentLinkedQueue、ConcurrentHashMap等。 -
使用
synchronized关键字或Lock对象手动同步ArrayList的操作。
需要注意的是,以上方法在提供线程安全的同时,可能会带来一定的性能开销。
- 问题 9. 简述 ArrayList 和 LinkedList 的区别
解答:ArrayList 和 LinkedList 都是 List 接口的实现,但它们在内部数据结构和性能上有一些区别:
- 内部数据结构:
ArrayList是基于动态数组实现的,支持随机访问,按索引访问元素非常快,时间复杂度为 O(1)。LinkedList是基于双向链表实现的,不支持高效的随机访问,按索引访问元素需要从头(或尾)开始遍历,时间复杂度为 O(n)。 - 插入和删除:
ArrayList的插入和删除操作需要进行数组元素的移动(除非插入和删除操作在列表末尾进行),所以插入和删除元素的时间复杂度为 O(n)。LinkedList的插入和删除操作只需要改变节点的引用,所以在列表中间插入和删除元素的时间复杂度为 O(1)(前提是已经获取到了要插入位置的节点)。 - 内存占用:
ArrayList的内存占用相对较低,因为它只需要存储元素数据和数组的引用。LinkedList的内存占用较高,因为它需要额外存储节点之间的引用。
总的来说,ArrayList 更适合随机访问场景,LinkedList 更适合插入和删除操作频繁的场景。
- 问题 10. 简述 ArrayList 和 Vector 的区别
解答:ArrayList 和 Vector 都是实现了 List 接口的类,它们都是基于动态数组实现的,但是在同步性和性能上有一些区别:
- 同步性:
Vector是线程安全的,它的大部分方法都进行了同步处理,可以在多线程环境下使用。实现上其实就是 Vector 在 ArrayList 的方法前面加上了 Synchronized。ArrayList是非线程安全的,它的方法没有进行同步处理,所以在多线程环境下可能会出现问题。 - 性能:由于
Vector进行了同步处理,所以在单线程环境下,Vector的性能会比ArrayList差一些。ArrayList在单线程环境下的性能比Vector好,因为它没有进行同步处理。 - 扩容:
ArrayList在每次需要扩容时,都会增加到原来的 1.5 倍。Vector在每次需要扩容时,都会增加到原来的 2 倍。
总的来说,如果需要在多线程环境下使用,可以选择 Vector,如果是在单线程环境下,或者已经通过其他方式处理了同步问题,那么 ArrayList 会是更好的选择。
- 问题 11. 简述 ArrayList 与 Array 的区别
解答:Array 和 ArrayList 是 Java 中两种不同的数据结构,它们的主要区别在于大小的可变性、性能、类型限制和功能。
- 大小可变性:
Array是固定长度的,一旦创建,其大小就不能改变。ArrayList是动态的,可以自动调整其大小以适应元素的添加和删除。 - 性能:
Array在访问元素时具有更好的性能,因为它是基于索引的数据结构。ArrayList在添加和删除元素时具有更好的性能,特别是在列表的末尾,因为它可以动态调整大小。 - 类型限制:
Array可以存储基本数据类型或对象。ArrayList只能存储对象,不能直接存储基本数据类型。 - 功能:
Array是一个简单的数据结构,没有提供很多功能。ArrayList是一个集合类,提供了大量的方法,如添加、删除、遍历等。
总的来说,Array 和 ArrayList 各有优势,选择哪种取决于具体的需求。
- 问题 12. 介绍一下 LinkedList 的底层结构和相关原理
解答:LinkedList 是 Java 中常用的一种链表实现,其底层是基于双向链表实现的。
-
存储结构:
LinkedList内部使用一个双向链表来存储元素。每个元素(节点)都包含了对前一个元素和后一个元素的引用。 -
插入和删除:
LinkedList在链表头部和尾部插入和删除元素非常高效,时间复杂度为 O(1)。在链表中间插入和删除元素需要先找到对应的位置,时间复杂度为 O(n)。 -
访问元素:
LinkedList不支持高效的随机访问,访问特定索引的元素需要从头(或尾)开始遍历,时间复杂度为 O(n)。 -
线程安全:
LinkedList是非线程安全的,如果需要在多线程环境下使用,可以使用Collections.synchronizedList()方法返回一个线程安全的LinkedList。
总的来说,LinkedList 是一种链表结构,适合插入和删除操作频繁的场景,但在访问元素时效率较低。
- 问题 13. 介绍一下 Vector 的底层结构和相关原理
解答:Vector 是 Java 中的一种线程安全的动态数组实现,其底层是基于数组实现的。
-
存储结构:
Vector内部使用一个数组(elementData)来存储元素。当添加元素时,如果数组已满,就会创建一个新的更大的数组,并将原数组的内容复制到新数组中,这个过程称为扩容。 -
扩容机制:
Vector的扩容机制是,每次扩容时,新数组的大小是原数组大小的 2 倍。如果这个值仍然不足以满足需求,那么新数组的大小就直接设置为需求的大小。 -
插入和删除:
Vector在尾部插入和删除元素非常高效,时间复杂度为 O(1)。但是在中间或头部插入和删除元素需要移动大量元素,时间复杂度为 O(n)。 -
访问元素:由于底层是数组,所以
Vector支持随机访问,按索引访问元素的时间复杂度为 O(1)。 -
线程安全:
Vector的所有公共方法都进行了同步处理,所以它是线程安全的。但这也意味着在单线程环境下,Vector的性能会比ArrayList差一些。
总的来说,Vector 是一种线程安全的动态数组结构,适合在多线程环境下使用,但在单线程环境下,由于同步处理带来的开销,其性能会比 ArrayList 差一些。
- 问题 14. 介绍一下 Stack 的底层结构和相关原理
解答:Stack 是 Java 中的一种后进先出(LIFO)的数据结构,其底层是基于 Vector 实现的。
-
存储结构:
Stack内部使用一个Vector来存储元素。当添加元素(压栈)时,元素被添加到Vector的末尾;当删除元素(弹栈)时,元素从Vector的末尾被移除。 -
扩容机制:由于
Stack是基于Vector实现的,所以其扩容机制与Vector相同。每次扩容时,新数组的大小是原数组大小的 2 倍。 -
插入和删除:
Stack的插入和删除操作都在Vector的末尾进行,所以非常高效,时间复杂度为 O(1)。 -
访问元素:
Stack提供了peek()方法来查看栈顶元素,时间复杂度为 O(1)。由于底层是Vector,所以Stack也支持按索引访问元素,但这并不常用。 -
线程安全:由于
Stack是基于Vector实现的,所以它也是线程安全的。但这也意味着在单线程环境下,Stack的性能会比基于ArrayList实现的Deque差一些。
总的来说,Stack 是一种后进先出的数据结构,适合在需要后进先出操作的场景下使用,如函数调用栈、回溯算法等。
- 问题 15. 请解释一下 Java 中的 CopyOnWriteArrayList
解答:CopyOnWriteArrayList 是 Java 中的一个线程安全的 List 实现,它是通过“写时复制”(Copy-On-Write)策略来保证并发安全的。
-
写时复制策略:当对
CopyOnWriteArrayList进行修改操作(如add、set、remove等)时,它并不直接在当前数组上进行修改,而是先将当前数组进行复制,然后在新的数组上进行修改,最后再将引用指向新的数组。这样可以保证在修改过程中不会影响到读操作,实现了读写分离。 -
读操作无锁:由于所有的写操作都是在新的数组上进行的,所以读操作是无锁的,可以直接读取,这对于读多写少的场景性能提升很大。
-
写操作加锁:写操作(修改、添加、删除等)需要加锁,防止多线程同时写入时导致数据不一致。
-
内存占用:由于每次写操作都需要复制一个新的数组,所以
CopyOnWriteArrayList在内存占用上会比普通的ArrayList大。
总的来说,CopyOnWriteArrayList 是一种适用于读多写少且需要线程安全的场景的 List 实现。但是由于写时复制策略,它在内存占用和写操作性能上有一定的开销。
2.3、JavaQueue集合相关
- 问题 16. 简述队列和栈是什么,它们有何区别?
解答:队列(Queue)和栈(Stack)是两种常见的数据结构,它们在数据的存储和访问方式上有一些区别。
-
队列(Queue):队列是一种先进先出(FIFO,First In First Out)的数据结构,新元素添加到队列的尾部,而移除元素则从队列的头部开始。队列常用于实现需要按照元素添加顺序进行处理的场景,如任务队列、消息队列等。
-
栈(Stack):栈是一种后进先出(LIFO,Last In First Out)的数据结构,新元素添加到栈的顶部,移除元素也从栈的顶部开始。栈常用于实现需要后进先出操作的场景,如函数调用栈、撤销操作、深度优先搜索等。
总的来说,队列和栈的主要区别在于元素的访问顺序:队列是先进先出,而栈是后进先出。
- 问题 17. 请解释一下 Java 中的 Queue 和 Deque?
Queue 和 Deque 是 Java 中的两种接口,分别代表队列和双端队列这两种数据结构。
-
Queue:队列是一种先进先出(FIFO)的数据结构,支持在队尾插入元素(offer方法),在队头删除元素(poll方法),查看队头元素(peek方法)。常用的Queue实现类有LinkedList、PriorityQueue等。 -
Deque:双端队列是一种特殊的队列,它支持在两端插入和删除元素。除了支持Queue的所有操作外,还支持在队头插入元素(offerFirst方法),在队尾删除元素(pollLast方法),查看队尾元素(peekLast方法)。常用的Deque实现类有ArrayDeque、LinkedList等。
在实际使用时,可以根据具体需求选择使用 Queue 还是 Deque,以及选择合适的实现类。例如,如果需要按元素的优先级进行处理,可以使用 PriorityQueue;如果需要在两端插入和删除元素,可以使用 Deque。
- 问题 18. 请解释一下 Java 中的 PriorityQueue?
解答:PriorityQueue 是 Java 中的一种特殊的队列,它的特点是队列中的元素按照它们的优先级进行排序。
-
存储结构:
PriorityQueue内部使用一个二叉小顶堆来实现。二叉小顶堆是一种特殊的二叉树,树中任意一个非叶子节点的值都不大于其子节点的值,树的根节点(顶部)是所有节点中的最小值。 -
元素排序:
PriorityQueue中的元素可以自然排序,或者通过提供的Comparator进行自定义排序。在添加元素时,会根据元素的优先级找到合适的位置保证堆的性质。 -
插入和删除:插入元素和删除元素(或者说是获取并删除最高优先级的元素)的时间复杂度都是 O(log n)。
-
访问元素:
PriorityQueue提供了peek方法来访问最高优先级的元素(堆顶元素),时间复杂度为 O(1)。 -
线程安全:
PriorityQueue是非线程安全的,如果需要在多线程环境下使用,可以使用PriorityBlockingQueue。
总的来说,PriorityQueue 是一种可以动态调整内部元素顺序的队列,适合实现需要动态排序的场景,如任务调度、Dijkstra 算法、Huffman 编码等。
- 问题 19. 请解释一下 Java 中的 BlockingQueue?
解答:BlockingQueue 是 Java 中的一个接口,它是一种特殊的队列,主要提供了阻塞操作的支持,适用于生产者消费者模式。
-
阻塞操作:
BlockingQueue提供了put和take方法,当队列满时,put方法会阻塞直到队列不满;当队列空时,take方法会阻塞直到队列不空。这样可以简化生产者消费者模式的实现,无需显式地进行同步和唤醒操作。 -
非阻塞操作:
BlockingQueue也提供了非阻塞操作offer和poll,如果无法立即执行操作,这些方法会返回一个特殊值(如null或false)而不是阻塞。 -
超时操作:
BlockingQueue还提供了带超时的offer和poll方法,如果在指定的时间内无法执行操作,这些方法会返回一个特殊值。 -
实现类:
BlockingQueue的常用实现类有ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等,它们分别适用于不同的场景。
总的来说,BlockingQueue 是一种支持阻塞操作的队列,适合实现生产者消费者模式,可以简化多线程编程。
2.4、JavaSet集合相关
- 问题 20. 介绍一下 HashSet 的底层结构和相关原理
解答:HashSet 是基于 HashMap 实现的,底层采用 HashMap 来保存所有元素。因此,HashSet 的数据结构就是 HashMap 的数据结构。
HashMap 是一个散列表,它存储的内容是键值对 (key-value)。HashMap 通过键的哈希值进行快速查找,具有较高的查找和插入速度。
HashSet 中的元素实际上作为 HashMap 的键存在,而 HashMap 的值则存储了一个固定的对象 PRESENT。因此,HashSet 中的元素不能重复,这是因为 HashMap 的键不能重复。
HashSet 的操作都是基于 HashMap 的操作来实现的,例如添加元素、删除元素、查找元素等。
- 问题 21. 介绍一下 LinkedHashSet 的底层结构和相关原理
解答:LinkedHashSet 是 HashSet 的一个子类,它的底层是基于 LinkedHashMap 来实现的。
LinkedHashMap 是 HashMap 的一个子类,它在 HashMap 的基础上,增加了一个双向链表。这个双向链表连接了所有的键值对,定义了键值对的迭代顺序。迭代的顺序可以是插入顺序,也可以是访问顺序。
LinkedHashSet 中的元素实际上作为 LinkedHashMap 的键存在,而 LinkedHashMap 的值则存储了一个固定的对象 PRESENT。因此,LinkedHashSet 中的元素不能重复,这是因为 LinkedHashMap 的键不能重复。
LinkedHashSet 的操作都是基于 LinkedHashMap 的操作来实现的,例如添加元素、删除元素、查找元素等。由于 LinkedHashSet 维护了一个运行于所有条目的双向链表,因此,可以在用迭代器遍历 LinkedHashSet 时,得到一个确定的顺序(插入的顺序)。
- 问题 22. 介绍一下 TreeSet 的底层结构和相关原理
解答:TreeSet 是基于 TreeMap 实现的,底层采用 TreeMap 来保存所有元素。因此,TreeSet 的数据结构就是 TreeMap 的数据结构。
TreeMap 是一个红黑树(自平衡的排序二叉树)。它存储的内容是键值对 (key-value)。TreeMap 通过键的自然顺序或者自定义的比较器进行排序,具有较高的查找和插入速度。
TreeSet 中的元素实际上作为 TreeMap 的键存在,而 TreeMap 的值则存储了一个固定的对象 PRESENT。因此,TreeSet 中的元素不能重复,这是因为 TreeMap 的键不能重复。
TreeSet 的操作都是基于 TreeMap 的操作来实现的,例如添加元素、删除元素、查找元素等。由于 TreeSet 是基于 TreeMap 实现的,所以 TreeSet 的元素是有序的,元素的排序方式取决于构造 TreeSet 时提供的 Comparator,或者依赖元素的自然顺序(Comparable)。
TreeSet 是 SortedSet 接口的一个实现类,它提供了一个基于树结构的 Set,元素可以按照自然顺序或者自定义的比较器进行排序。
- 问题 23. 请解释一下 Java 中的 EnumSet?
解答:EnumSet 是 Java 中的一个专门为枚举类型设计的集合类。它继承自 AbstractSet,并实现了 Set 接口。
以下是 EnumSet 的一些特性:
-
EnumSet中的所有元素都必须来自同一个枚举类型,它在创建时显式或隐式地指定。 -
EnumSet是有序的,其元素的顺序就是它们在源代码中的顺序。 -
EnumSet集合类的实现是非常高效和快速的,其大部分操作都是通过位运算实现的。 -
EnumSet不允许使用null元素,如果尝试添加null元素,它会抛出NullPointerException。 -
EnumSet是线程不安全的,如果多个线程同时修改EnumSet,需要进行同步处理。
以下是创建 EnumSet 的一些方法:
EnumSet.allOf(Class<E> elementType):创建一个包含指定枚举类型的所有元素的EnumSet。EnumSet.noneOf(Class<E> elementType):创建一个指定枚举类型的空EnumSet。EnumSet.of(E first, E... rest):创建一个最初包含指定元素的EnumSet。EnumSet.range(E from, E to):创建一个包含从from元素到to元素范围内的所有元素的EnumSet。EnumSet.copyOf(Collection<E> c)或EnumSet.copyOf(EnumSet<E> s):创建一个与指定EnumSet具有相同元素类型的EnumSet,最初包含相同的元素(如果有的话)。
- 问题 24. 请解释一下 Java 中的 SortedSet
解答:SortedSet 是 Java 集合框架中的一个接口,它继承自 Set 接口。SortedSet 接口为集合中的元素提供了一个总的排序。
以下是 SortedSet 的一些特性:
-
SortedSet中的元素按照自然顺序或者自定义的比较器(Comparator)进行排序。 -
SortedSet不允许插入null元素。如果尝试插入null元素,它会抛出NullPointerException。 -
SortedSet是线程不安全的,如果多个线程同时修改SortedSet,需要进行同步处理。
以下是 SortedSet 的一些主要方法:
-
Comparator<? super E> comparator():返回用于对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回null。 -
E first():返回此 set 中当前第一个(最低)元素。 -
SortedSet<E> headSet(E toElement):返回此 set 的部分视图,其元素严格小于toElement。 -
E last():返回此 set 中当前最后一个(最高)元素。 -
SortedSet<E> subSet(E fromElement, E toElement):返回此 set 的部分视图,其元素的范围从fromElement(包括)到toElement(不包括)。 -
SortedSet<E> tailSet(E fromElement):返回此 set 的部分视图,其元素大于等于fromElement。
- 问题 25. 请解释一下 Java 中的 NavigableSet
解答:NavigableSet 是 Java 集合框架中的一个接口,它继承自 SortedSet 接口。NavigableSet 描述了一种可以通过搜索方法导航的数据结构。
以下是 NavigableSet 的一些特性:
-
NavigableSet中的元素按照自然顺序或者自定义的比较器(Comparator)进行排序。 -
NavigableSet提供了多种导航方法,例如获取小于/大于某个元素的最大/最小元素等。 -
NavigableSet不允许插入null元素。如果尝试插入null元素,它会抛出NullPointerException。 -
NavigableSet是线程不安全的,如果多个线程同时修改NavigableSet,需要进行同步处理。
以下是 NavigableSet 的一些主要方法:
-
E lower(E e):返回此 set 中严格小于给定元素的最大元素;如果不存在这样的元素,则返回null。 -
E floor(E e):返回此 set 中小于等于给定元素的最大元素;如果不存在这样的元素,则返回null。 -
E ceiling(E e):返回此 set 中大于等于给定元素的最小元素;如果不存在这样的元素,则返回null。 -
E higher(E e):返回此 set 中严格大于给定元素的最小元素;如果不存在这样的元素,则返回null。 -
E pollFirst():获取并移除此 set 中的第一个(最低)元素;如果此 set 为空,则返回null。 -
E pollLast():获取并移除此 set 中的最后一个(最高)元素;如果此 set 为空,则返回null。
TreeSet 是 NavigableSet 接口的一个实现类,它提供了一个基于树结构的 Set,元素可以按照自然顺序或者自定义的比较器进行排序。