news 2026/4/15 14:42:11

Java集合面试题最新整理,持续更新

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java集合面试题最新整理,持续更新

1.说说Java中常用的容器有哪些?


容器主要包括CollectionMap两种,Collection 存储着对象的集合 ,而 Map 存储着键值对(两个对象)的映射表。

如图:

👨‍💻面试官追问:说说集合有哪些类及他们各自的区别和特点?

  • Set

  • TreeSet基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。

  • HashSet基于HashMap实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。

  • LinkedHashSet是 HashSet 的子类,并且其内部是通过 LinkedHashMap 来实现的。内部使用双向链表维护元素的插入顺序。

  • List

  • ArrayList基于动态数组实现,支持随机访问。

  • Vector和 ArrayList 类似,但它是线程安全的。

  • LinkedList基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。

  • Queue

  • LinkedList可以用它来实现双向队列。

  • PriorityQueue基于堆结构实现,可以用它来实现优先队列。

  • ArrayQueue基于数组实现,可以用它实现双端队列,也可以作为栈。

👨‍💻面试官追问:说说Map有哪些类及他们各自的区别和特点?

  • TreeMap基于红黑树实现。

  • HashMap1.7基于数组+链表实现,1.8基于数组+链表+红黑树。链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。

  • HashTable和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。(现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高(1.7 ConcurrentHashMap 引入了分段锁, 1.8 引入了红黑树)。)

  • LinkedHashMap继承自 HashMap。使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。

2.详细说说 Arraylist 和 LinkedList的区别?


  • ArrayList:底层是基于数组实现的,查找快,增删较慢LinkedList不支持高效的随机元素访问,而ArrayList支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。

  • LinkedList:底层是基于链表实现的。确切的说是循环双向链表(JDK1.6之前是双向循环链表、JDK1.7之后取消了循环),查找慢、增删快。LinkedList链表由一系列表项连接而成,一个表项包含3个部分︰元素内容、前驱表和后驱表。因此内存空间占用比ArrayList 更多。

👨‍💻面试官追问:ArrayList的增删一定比LinkedList要慢吗?

不一定的。

  1. 如果增删都是在末尾来操作(每次调用的都是remove()add()),此时 ArrayList就不需要移动和复制数组来进行操作了。如果数据量有百万级的时,速度是会比 LinkedList 要快的。

  2. 如果删除操作的位置是在中间。由于LinkedList的消耗主要是在遍历上,ArrayList的消耗主要是在移动和复制上(底层调用的是arrayCopy()方法,是native方法)。LinkedList 的遍历速度是要慢于ArrayList的复制移动速度的。如果数据量有百万级的时,还是ArrayList要快。

3.ArrayList实现 RandomAccess接口有何作用?


public interface RandomAccess {

}

查看源码我们发现实际上RandomAccess接口中什么都没有定义。

从源码可以看出RandomAccess 接口只是一个标志接口,只要List集合实现这个接口,就能支持快速随机访问。通过查看Collections类中的binarySearch()方法,可以看出,判断List是否实现RandomAccess接口来实行indexedBinarySerach(list, key)iteratorBinarySerach(list, key)方法。再通过查看这两个方法的源码发现:实现RandomAccess接口的List集合采用一般的 for循环遍历,而未实现这接口则采用迭代器,即ArrayList 一般采用for循环遍历,而 LinkedList 一般采用迭代器遍历

public static

int binarySearch(List<? extends Comparable<? super T>> list, T key) {

if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)

return Collections.indexedBinarySearch(list, key);

else

return Collections.iteratorBinarySearch(list, key);

}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、场景题、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案【点击此处即可/免费获取】​https://docs.qq.com/doc/DQXdYWE9LZ2ZHZ1ho

👨‍💻面试官追问:为何LinkedList却没实现这个接口?

ArrayList底层是数组,而LinkedList底层是链表。

数组天然支持随机访问,时间复杂度为 O(1),所以称为快速随机访问。链表需要遍历到特定位置才能访问特定位置的元素,时间复杂度为 O(n),所以不支持快速随机访问。

ArrayList实现了RandomAccess接口,就表明了他具有快速随机访问功能。RandomAccess接口只是标识,并不是说ArrayList实现RandomAccess接口才具有快速随机访问功能的!

4.说一说Vector 和 ArrayList 的区别?


他们两个都实现了List接口。底层数据结构都是数组。

不同的是:

  1. vector通过removeadd等方法加上synchronized关键字实现线程同步,所以是线程安全的。而ArrayList是线程不安全的

  2. 由于vector使用了synchronized进行加锁,所以性能不如ArrayList

  3. Vector 扩容时,如果未指定扩容递增值capacityIncrement,或该值不大于 0 时,每次扩容为原来的2倍,否则扩容量为capacityIncrement的值。ArrayList每次扩容为旧容量的1.5

5.说说ArrayList 的扩容机制?


  1. 当使用add方法的时候首先调用ensureCapacityInternal方法,传入size+1进去,检查是否需要扩容

  2. 如果空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA,就初始化为默认大小10,获取“默认的容量”和要扩容的大小两者之间的最大值

  3. 和当前数组长度比较,如果if (minCapacity - elementData.length > 0)执行grow扩容方法

  4. 将数组扩容为原来的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);

  5. 检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量

  6. 再检查新容量newCapacity 是否超出了ArrayList所定义的最大容量,若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE,如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE(MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8

  7. ArrayList 中copy数组的核心就是System.arraycopy方法,将original数组的所有数据复制到copy数组中,这是一个本地方法

详细的扩容源码可以参考:https://blog.csdn.net/qq_45966440/article/details/122270715?spm=1001.2014.3001.5501

6.Array和ArrayList有何区别?


  • Array可以容纳基本类型和对象,而ArrayList只能容纳对象

  • Array是指定大小的,ArrayList 的容量是根据需求自动扩展

  • ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等

👨‍💻面试官追问:什么时候更适合使用Array?

  1. 如果列表的大小已经指定,大部分情况下是存储和遍历它们可以使用Array

  2. 对于基本类型数据,ArrayList 使用自动装箱来减少编码工作量;而当处理固定大小的基本数据类型的时候,这种方式相对比较慢,这时候应该使用Array

  3. 如果你要使用多维数组,使用[][]比 List更容易

7.遍历一个List有哪些不同的方式?


先说一下常见的元素在内存中的存储方式,主要有两种:

  1. 顺序存储(Random Access):相邻的数据元素在内存中的位置也是相邻的,可以根据元素的位置读取元素。

  2. 链式存储(Sequential Access):每个数据元素包含它下一个元素的内存地址,在内存中不要求相邻。例如LinkedList。

主要的遍历方式主要有三种:

  1. for循环遍历:遍历者自己在集合外部维护一个计数器,依次读取每一个位置的元素

  2. Iterator遍历:基于顺序存储集合的Iterator可以直接按位置访问数据。基于链式存储集合的Iterator,需要保存当前遍历的位置,然后根据当前位置来向前或者向后移动指针

  3. foreach遍历:也就是增强for循环,foreach内部也是采用了Iterator的方式实现,但使用时不需要显示地声明Iterator

代码如下:

public class TestLinkedList {

public static void main(String[] args) {

List list = new ArrayList<>();

list.add (“aaa”);

list.add(“bbb”);

list.add(“ccc”);

//for循环遍历

for (int i = 0; i < list.size(); i++) {

System .out.println(list.get(i));

}

//Iterator遍历

Iterator iterator = list.iterator();

while (iterator.hasNext()) {

System.out.println(iterator.next());

}

//foreach遍历

for (String s : list) {

System.out.println(s);

}

}

}

👨‍💻面试官追问:那么对于以上三种遍历方式应该如何选取呢?

在Java集合框架中,提供了一个RandomAccess接口,该接口没有方法,只是一个标记。通常用来标记List的实现是否支持RandomAccess。所以在遍历时,可以先判断是否支持RandomAccess ( list instanceof RandomAccess),如果支持可用for循环遍历,否则建议用Iterator或 foreach遍历。

8.comparable和comparator的区别?


  • comparable接口出自java.lang包,可以理解为一个内比较器,因为实现了comparable接口的类可以和自己比较,要和其他实现了Comparable接口类比较,可以使用compareTo(objectobj)方法。compareTo方法的返回值是int,有三种情况:
  1. 返回正整数(比较者大于被比较者)

  2. 返回0(比较者等于被比较者)

  3. 返回负整数(比较者小于被比较者)

  • comparator接口出自java.util包,它有一个compare(object obj1,object obj2)方法用来排序,返回值同样是int,有三种情况,和compareTo类似。

它们之间的区别:

  1. 很多包装类都实现了comparable接口,像Integer、string等。所以直接调用co1lections.sort()直接可以使用。如果对类里面自带的自然排序不满意,而又不能修改其源代码的情况下,使用comparator就比较合适。

  2. 此外使用comparator可以避免添加额外的代码与我们的目标类耦合,同时可以定义多种排序规则,这一点是comparable接口没法做到的

  3. 从灵活性和扩展性讲Comparator更优,故在面对自定义排序的需求时,可以优先考虑使用comparator接口。

9.Collection和Collections有什么区别?


  • Collection:是最基本的集合接口,它提供了对集合对象进行基本操作的通用接口方法。一个Collection代表一组Object,即Collection的元素。它的直接继承接口有List,Set 和Queue。

  • Collections:是不属于Java的集合框架的,它是集合类的一个工具类此类不能被实例化服务于Java的Collection框架。它包含有关集合操作的静态多态方法,实现对各种集合的搜索、排序、线程安全等操作。

10.说一下PriorityQueue?

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、场景题、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案【点击此处即可/免费获取】​https://docs.qq.com/doc/DQXdYWE9LZ2ZHZ1ho


PriorityQueue是在 JDK1.5 中被引入的, 其与Queue的区别在于元素出队顺序是与优先级相关的,即总是优先级最高的元素先出队。

它有这些特点:

  • PriorityQueue利用了二叉堆的数据结构来实现的,底层使用可变长的数组来存储数据

  • PriorityQueue通过堆元素的上浮和下沉,实现了在O(logn)的时间复杂度内插入元素和删除堆顶元素。

  • PriorityQueue非线程安全的,且不支持存储NULLnon-comparable的对象。

  • PriorityQueue默认是小顶堆,但可以接收一个Comparator作为构造参数,从而来自定义元素优先级的先后。

  • 默认容量是11。当数组比较小(小于64)的时候每次扩容容量翻倍。当数组比较大(大于等于64)的时候每次扩容只增加一半的容量

  • PriorityQueue不是有序的,只有堆顶存储着最小的元素

可以参考PriorityQueue源码:https://blog.csdn.net/qq_45966440/article/details/122273598?spm=1001.2014.3001.5501

11.说一下HashSet的实现原理?


HashSet的实现是依赖于HashMap的,HashSet 的值都是存储在HashMap中的。在 HashSet 的构造法中会初始化一个HashMap对象,HashSet 不允许值重复。因此,HashSet的值是作为HashMap的key存储在HashMap 中的,当存储的值已经存在时返回false。

👨‍💻面试官追问:HashSet有哪些特点?

  • 无序性(存储元素无序)

  • 唯一性(允许使用null)本质上,HashSet的值是作为HashMap的key存储在HashMap 中的,因此保证唯一性

  • HashSet没有提供get()方法,同HashMap一样,因为Set内部是无序的,所以只能通过迭代的方式获得

12.HashMap的实现原理/底层数据结构?


  • JDK1.7:数组 + 链表

  • JDK1.8:数组 + (链表 | 红黑树)

HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。

所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。

JDK1.8的hash方法:

static final int hash(Object key) {

int h;

// key.hashCode():返回散列值也就是hashcode

// ^ :按位异或

// >>>:无符号右移,忽略符号位,空位都以0补齐

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

JDK1.7的hash方法:

static int hash(int h) {

h ^= (h >>> 20) ^ (h >>> 12);

return h ^ (h >>> 7) ^ (h >>> 4);

}

从源码可以看出JDK 1.8 的 hash 方法 相比于 JDK 1.7 hash 方法更加简化,但是原理不变。JDK 1.7 的 hash 方法的性能会稍差一点点,因为毕竟扰动了 4 次。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 14:40:39

嵌入式开发者的福音:nanoMODBUS让工业通信变得简单高效

嵌入式开发者的福音&#xff1a;nanoMODBUS让工业通信变得简单高效 【免费下载链接】nanoMODBUS A compact MODBUS RTU/TCP C library for embedded/microcontrollers 项目地址: https://gitcode.com/gh_mirrors/na/nanoMODBUS 还在为嵌入式设备上实现Modbus通信而烦恼吗…

作者头像 李华
网站建设 2026/4/15 14:38:45

从原理到实践:Halcon矩形角点检测的8种算法深度解析(2024最新版)

从原理到实践&#xff1a;Halcon矩形角点检测的8种算法深度解析&#xff08;2024最新版&#xff09; 在工业视觉检测领域&#xff0c;矩形物体的精准定位一直是核心挑战。作为机器视觉领域的标杆工具&#xff0c;Halcon提供了多达8种不同的角点检测算法&#xff0c;每种方法背后…

作者头像 李华
网站建设 2026/4/15 14:38:37

避坑指南:Protege转Neo4j时,为什么你的Turtle文件总是0KB?

避坑指南&#xff1a;Protege转Neo4j时&#xff0c;为什么你的Turtle文件总是0KB&#xff1f; 当你满怀期待地将Protege中的OWL本体文件转换为Turtle格式&#xff0c;准备导入Neo4j构建知识图谱时&#xff0c;却发现生成的Turtle文件大小显示为0KB——这种挫败感&#xff0c;相…

作者头像 李华
网站建设 2026/4/15 14:34:23

【Syncfusion系列】从零到一:解锁企业级WPF应用开发新利器

1. 为什么选择Syncfusion开发企业级WPF应用&#xff1f; 第一次接触Syncfusion时&#xff0c;我正为一个金融项目寻找能快速实现复杂数据可视化的方案。当时试用了市面上多个UI框架&#xff0c;直到发现Syncfusion的WPF控件库——那种"终于找对了工具"的感觉至今记忆…

作者头像 李华