常见 JAVA 集合面试题整理 自用版持续更新

本文涉及的产品
可观测监控 Prometheus 版,每月50GB免费额度
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 这是一份详尽的Java集合面试题总结,涵盖ArrayList与LinkedList、HashMap与HashTable、HashSet与TreeSet的区别,以及ConcurrentHashMap的实现原理。内容从底层数据结构、性能特点到应用场景逐一剖析,并提供代码示例便于理解。此外,还介绍了如何遍历HashMap和HashTable。无论是初学者还是进阶开发者,都能从中受益。代码资源可从[链接](https://pan.quark.cn/s/14fcf913bae6)获取。

我整合了多个技术平台上的相关内容,从常见问题入手,结合应用实例,为你梳理出这篇Java集合面试题总结,希望能助你学习一臂之力。

常见JAVA集合面试题(自用整理,持续更新)

一、ArrayList和LinkedList的区别

1. 底层数据结构

  • ArrayList:基于动态数组实现。在创建ArrayList时,如果没有指定初始容量,会默认创建一个容量为10的数组。当数组中的元素数量超过数组容量时,会进行扩容操作,新的容量一般是原容量的1.5倍。例如:
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
// 当继续添加元素时,如果当前容量不足,会自动扩容
  • LinkedList:基于双向链表实现。每个节点不仅存储了元素本身,还包含了指向前一个节点和后一个节点的引用。比如:
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("a");
linkedList.add("b");
// 链表节点之间通过引用相互连接

2. 随机访问性能

  • ArrayList:支持快速随机访问,时间复杂度为O(1)。因为可以通过数组索引直接定位到元素的位置。例如:
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
int element = list.get(1); // 可以快速获取索引为1的元素,即20
  • LinkedList:随机访问性能较差,时间复杂度为O(n)。因为需要从链表的头节点或尾节点开始,逐个遍历节点,直到找到目标位置的节点。例如:
LinkedList<Integer> linkedList = new LinkedList<>();
linkedList.add(10);
linkedList.add(20);
linkedList.add(30);
// 获取索引为1的元素,需要从链表头开始遍历
int element = linkedList.get(1);

3. 插入和删除性能

  • ArrayList:在数组中间位置插入或删除元素时,需要移动大量元素,时间复杂度为O(n)。但在数组尾部插入或删除元素时,时间复杂度为O(1)(不考虑扩容情况)。例如:
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
// 在索引为1的位置插入元素4,后续元素都要向后移动
list.add(1, 4);
  • LinkedList:在链表的任意位置插入或删除元素,只需要修改相邻节点的引用,时间复杂度为O(1)。但如果要先定位到指定位置的节点,时间复杂度则为O(n)。例如:
LinkedList<Integer> linkedList = new LinkedList<>();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
// 在索引为1的位置插入元素4,只需修改相邻节点引用
linkedList.add(1, 4);

4. 内存占用

  • ArrayList:内存占用相对较小,主要存储元素本身。
  • LinkedList:每个节点除了存储元素,还需要存储两个引用,内存占用较大。

二、HashMap和HashTable的区别

1. 线程安全性

  • HashMap:非线程安全,在多线程环境下并发访问可能会出现数据不一致问题,甚至引发死循环(在JDK 1.7及之前的版本中,多线程扩容时可能会出现)。例如:
HashMap<Integer, String> hashMap = new HashMap<>();
// 多线程同时进行put操作,可能会出现问题
new Thread(() -> {
   
    hashMap.put(1, "a");
}).start();
new Thread(() -> {
   
    hashMap.put(2, "b");
}).start();
  • HashTable:线程安全,它的大部分方法(如put、get等)都使用了synchronized关键字进行同步。例如:
Hashtable<Integer, String> hashtable = new Hashtable<>();
// 多线程环境下使用相对安全
new Thread(() -> {
   
    hashtable.put(1, "a");
}).start();
new Thread(() -> {
   
    hashtable.put(2, "b");
}).start();

2. Null键和Null值

  • HashMap:允许null键和null值。例如:
HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(null, "nullValue");
hashMap.put(1, null);
  • HashTable:不允许null键和null值,否则会抛出NullPointerException。例如:
Hashtable<Integer, String> hashtable = new Hashtable<>();
// 以下代码会抛出NullPointerException
// hashtable.put(null, "value"); 
// hashtable.put(1, null);

3. 迭代器

  • HashMap:迭代器是fail - fast的,即当迭代过程中集合结构被修改(除了通过迭代器自身的remove方法),会立即抛出ConcurrentModificationException。例如:
HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "a");
hashMap.put(2, "b");
Iterator<Map.Entry<Integer, String>> iterator = hashMap.entrySet().iterator();
while (iterator.hasNext()) {
   
    Map.Entry<Integer, String> entry = iterator.next();
    // 这里如果直接调用hashMap.put(3, "c");会抛出异常
    iterator.remove();
}
  • HashTable:迭代器不是fail - fast的,在迭代过程中集合结构被修改,不会立即抛出异常,但可能导致迭代结果不准确。例如:
Hashtable<Integer, String> hashtable = new Hashtable<>();
hashtable.put(1, "a");
hashtable.put(2, "b");
Enumeration<Map.Entry<Integer, String>> enumeration = Collections.enumeration(hashtable.entrySet());
while (enumeration.hasMoreElements()) {
   
    Map.Entry<Integer, String> entry = enumeration.nextElement();
    // 这里如果调用hashtable.put(3, "c");不会立即抛出异常
}

4. 默认容量和扩容机制

  • HashMap:默认初始容量为16,负载因子为0.75。当HashMap中的元素个数超过容量 负载因子(即16 0.75 = 12)时,会进行扩容,新容量为原容量的2倍。例如:
HashMap<Integer, String> hashMap = new HashMap<>();
for (int i = 0; i < 13; i++) {
   
    hashMap.put(i, "value" + i);
    // 当添加到第13个元素时,会触发扩容
}
  • HashTable:默认初始容量为11,负载因子为0.75。当HashTable中的元素个数超过容量 负载因子(即11 0.75,取整后为8)时,会进行扩容,新容量为原容量的2倍 + 1(即11 * 2 + 1 = 23)。例如:
Hashtable<Integer, String> hashtable = new Hashtable<>();
for (int i = 0; i < 9; i++) {
   
    hashtable.put(i, "value" + i);
    // 当添加到第9个元素时,会触发扩容
}

三、HashSet和TreeSet的区别

1. 底层数据结构

  • HashSet:基于HashMap实现,内部使用HashMap来存储元素,HashSet的元素实际上是作为HashMap的键来存储的,值是一个固定的Object对象。例如:
HashSet<Integer> hashSet = new HashSet<>();
hashSet.add(1);
hashSet.add(2);
// 内部通过HashMap存储,键为1和2,值为固定对象
  • TreeSet:基于红黑树实现,红黑树是一种自平衡的二叉搜索树,能保证元素的有序性。例如:
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
// 内部通过红黑树存储,元素会按照从小到大的顺序排列

2. 元素顺序

  • HashSet:元素无序,元素的插入顺序和存储顺序不一定相同。例如:
HashSet<Integer> hashSet = new HashSet<>();
hashSet.add(3);
hashSet.add(1);
hashSet.add(2);
// 遍历hashSet时,元素顺序可能不是3, 1, 2
for (int i : hashSet) {
   
    System.out.print(i + " ");
}
  • TreeSet:元素有序,可以按照自然顺序(如数字从小到大、字符串按字典序)或通过实现Comparator接口来定义自定义顺序。例如:
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
// 遍历treeSet时,元素顺序是1, 2, 3
for (int i : treeSet) {
   
    System.out.print(i + " ");
}

3. 添加和查询性能

  • HashSet:添加和查询操作平均时间复杂度为O(1),因为使用哈希表进行快速查找。例如:
HashSet<Integer> hashSet = new HashSet<>();
hashSet.add(1);
boolean contains = hashSet.contains(1); // 快速判断是否包含元素1,时间复杂度O(1)
  • TreeSet:添加和查询操作时间复杂度为O(log n),因为红黑树的高度是对数级别的。例如:
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(1);
boolean contains = treeSet.contains(1); // 判断是否包含元素1,时间复杂度O(log n)

4. 唯一性保证

  • HashSet:通过hashCode()和equals()方法来保证元素的唯一性。当向HashSet中添加元素时,首先会计算元素的hashCode值,根据hashCode值确定元素在哈希表中的存储位置。如果该位置已经有元素存在,再通过equals()方法比较两个元素是否相等,如果相等则不添加。例如:
class CustomObject {
   
    private int id;
    public CustomObject(int id) {
   
        this.id = id;
    }
    @Override
    public int hashCode() {
   
        return id;
    }
    @Override
    public boolean equals(Object obj) {
   
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        CustomObject other = (CustomObject) obj;
        return id == other.id;
    }
}
HashSet<CustomObject> hashSet = new HashSet<>();
hashSet.add(new CustomObject(1));
hashSet.add(new CustomObject(1)); // 由于hashCode和equals方法判断为相同元素,不会重复添加
  • TreeSet:通过比较元素的顺序来保证唯一性,如果两个元素比较相等(通过自然顺序或自定义比较器判断),则不会同时存在于TreeSet中。例如:
class CustomComparator implements Comparator<Integer> {
   
    @Override
    public int compare(Integer o1, Integer o2) {
   
        return o1 - o2;
    }
}
TreeSet<Integer> treeSet = new TreeSet<>(new CustomComparator());
treeSet.add(1);
treeSet.add(1); // 由于比较器判断为相同元素,不会重复添加

四、ConcurrentHashMap的实现原理

1. JDK 1.7版本

  • 分段锁机制:ConcurrentHashMap将数据分成多个段(Segment),每个段相当于一个小的HashTable,都有自己独立的锁。在JDK 1.7中,默认有16个Segment。当一个线程对某个Segment进行写操作时,只会锁住这个Segment,其他线程仍然可以访问其他Segment,从而提高并发性能。例如:
ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();
// 多个线程可以同时对不同的Segment进行操作
new Thread(() -> {
   
    concurrentHashMap.put(1, "a");
}).start();
new Thread(() -> {
   
    concurrentHashMap.put(17, "b"); // 17和1可能位于不同的Segment,可并发操作
}).start();
  • 数据结构:每个Segment内部是一个数组 + 链表的结构,和HashMap类似。当发生哈希冲突时,通过链表来解决。例如:
// 假设某个Segment的数组长度为4
Segment<K, V> segment = new Segment<>(16, 0.75f);
// 当有元素put进来时,如果哈希冲突,会在链表上进行存储

2. JDK 1.8版本

  • CAS + Synchronized:摒弃了分段锁机制,采用CAS操作和Synchronized关键字相结合的方式。在JDK 1.8中,当多个线程进行读操作时,不需要加锁,可以直接并发进行。当进行写操作(如put)时,首先会尝试使用CAS操作来插入元素,如果CAS操作失败,再使用Synchronized关键字对当前节点进行加锁。例如:
ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();
// 多个线程读操作可并发
new Thread(() -> {
   
    String value = concurrentHashMap.get(1);
}).start();
new Thread(() -> {
   
    String value = concurrentHashMap.get(2);
}).start();
// 写操作先尝试CAS,失败后加锁
new Thread(() -> {
   
    concurrentHashMap.put(3, "c");
}).start();
  • 数据结构:和HashMap类似,采用数组 + 链表 + 红黑树的结构。当链表长度超过8时,会将链表转换为红黑树,以提高查找效率。例如:
ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();
// 当某个桶中的链表长度超过8时,会转换为红黑树
for (int i = 0; i < 10; i++) {
   
    concurrentHashMap.put(i, "value" + i);
    // 假设这些元素哈希冲突,都在同一个桶中,当添加到第9个元素时,链表可能转换为红黑树
}

五、如何遍历HashMap和HashTable

1. 遍历HashMap

(1)使用entrySet()遍历键值对

HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "a");
hashMap.put(2, "b");
for (Map.Entry<Integer, String> entry : hashMap.entrySet()) {
   
    Integer key = entry.getKey();
    String value = entry.getValue();
    System.out.println("Key: " + key + ", Value: " + value);
}

(2)使用keySet()遍历键,再通过键获取值

HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "a");
hashMap.put(2, "b");
for (Integer key : hashMap.keySet()) {
   
    String value = hashMap.get(key);
    System.out.println("Key: " + key + ", Value: " + value);
}

(3)使用values()遍历值(只能获取值,无法获取键)

HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "a");
hashMap.put(2, "b");
for (String value : hashMap.values()) {
   
    System.out.println("Value: " + value);
}

(4)使用迭代器遍历

HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "a");
hashMap.put(2, "b");
Iterator<Map.Entry<Integer, String>> iterator = hashMap.entrySet().iterator();
while (iterator.hasNext()) {
   
    Map.Entry<Integer, String> entry = iterator.next();
    Integer key = entry.getKey();
    String value = entry.getValue();
    System.out.println("Key: " + key + ", Value: " + value);
}

2. 遍历HashTable

(1)使用entrySet()遍历键值对

Hashtable<Integer, String> hashtable = new Hashtable<>();
hashtable.put(1, "a");
hashtable.put(2, "b");
for (Map.Entry<Integer, String> entry : hashtable.entrySet()) {
   
    Integer key = entry.getKey();
    String value = entry.getValue();
    System.out.println("Key: " + key + ", Value: " + value);
}

(2)使用keySet()遍历键,再通过键获取值

Hashtable<Integer, String> hashtable = new Hashtable<>();
hashtable.put(1, "a");
hashtable.put(2, "b");
for (Integer key : hashtable.keySet()) {
   
    String value = hashtable.get(key);
    System.out.println("Key: " + key + ", Value: " + value);
}

Java 集合,ArrayList,HashMap,LinkedList,HashSet,ConcurrentHashMap, 集合框架,List,Set,Map,Collection,Vector,TreeMap,LinkedHashSet,Queue



代码获取方式
https://pan.quark.cn/s/14fcf913bae6


相关文章
|
1月前
|
Java 测试技术 微服务
最新技术栈下 Java 面试高频技术点实操指南详解
本指南结合最新Java技术趋势,涵盖微服务(Spring Cloud Alibaba)、响应式编程(Spring WebFlux)、容器化部署(Docker+Kubernetes)、函数式编程、性能优化及测试等核心领域。通过具体实现步骤与示例代码,深入讲解服务注册发现、配置中心、熔断限流、响应式数据库访问、JVM调优等内容。适合备战Java面试,提升实操能力,助力技术进阶。资源链接:[https://pan.quark.cn/s/14fcf913bae6](https://pan.quark.cn/s/14fcf913bae6)
100 25
|
25天前
|
存储 安全 Java
2025 最新史上最全 Java 面试题独家整理带详细答案及解析
本文从Java基础、面向对象、多线程与并发等方面详细解析常见面试题及答案,并结合实际应用帮助理解。内容涵盖基本数据类型、自动装箱拆箱、String类区别,面向对象三大特性(封装、继承、多态),线程创建与安全问题解决方法,以及集合框架如ArrayList与LinkedList的对比和HashMap工作原理。适合准备面试或深入学习Java的开发者参考。附代码获取链接:[点此下载](https://pan.quark.cn/s/14fcf913bae6)。
185 48
|
1月前
|
存储 安全 Java
2025 年一线互联网大厂最新高质量 Java 面试八股文整理及答案汇总
本文整理了一线互联网大厂最新的高质量Java面试八股文及其答案,涵盖Java基础、集合框架与多线程三大核心模块。内容包括面向对象与面向过程的区别、`equals`与`==`的差异、`final`和`static`的用法、集合类如`ArrayList`与`LinkedList`的对比、`HashMap`的工作原理及其与`Hashtable`的区别,以及多线程中的线程创建方式、生命周期、上下文切换和死锁等知识点。通过系统化的梳理与解析,帮助读者高效备考Java面试,掌握核心技术要点。资源可从文末链接下载。
236 40
|
22天前
|
缓存 NoSQL Java
Java Redis 面试题集锦 常见高频面试题目及解析
本文总结了Redis在Java中的核心面试题,包括数据类型操作、单线程高性能原理、键过期策略及分布式锁实现等关键内容。通过Jedis代码示例展示了String、List等数据类型的操作方法,讲解了惰性删除和定期删除相结合的过期策略,并提供了Spring Boot配置Redis过期时间的方案。文章还探讨了缓存穿透、雪崩等问题解决方案,以及基于Redis的分布式锁实现,帮助开发者全面掌握Redis在Java应用中的实践要点。
70 6
|
25天前
|
NoSQL Java 微服务
2025 年最新 Java 面试从基础到微服务实战指南全解析
《Java面试实战指南:高并发与微服务架构解析》 本文针对Java开发者提供2025版面试技术要点,涵盖高并发电商系统设计、微服务架构实现及性能优化方案。核心内容包括:1)基于Spring Cloud和云原生技术的系统架构设计;2)JWT认证、Seata分布式事务等核心模块代码实现;3)数据库查询优化与高并发处理方案,响应时间从500ms优化至80ms;4)微服务调用可靠性保障方案。文章通过实战案例展现Java最新技术栈(Java 17/Spring Boot 3.2)的应用.
89 9
|
28天前
|
设计模式 安全 Java
Java 基础知识面试题全解析之技术方案与应用实例详解
本内容结合Java 8+新特性与实际场景,涵盖函数式编程、Stream API、模块化、并发工具等技术。通过Lambda表达式、Stream集合操作、Optional空值处理、CompletableFuture异步编程等完整示例代码,助你掌握现代Java应用开发。附面试题解析与技术方案,提升实战能力。代码示例涵盖计算器、员工信息统计、用户查询、模块化系统设计等,助你轻松应对技术挑战。
57 8
|
27天前
|
缓存 Java 索引
2025 年 Java 面试必备最常见 200 + 面试题及答案解析
这是一份针对Java面试的综合技术方案与应用实例文档,涵盖Java基础(JDK/JRE、字符串、IO流)、容器(ArrayList/LinkedList、HashMap)、多线程(线程创建、同步、线程池)、数据库(MySQL索引、Redis缓存穿透)及Spring框架(IOC容器、热部署)等核心模块。通过详细解析常见面试题,帮助读者深入理解并掌握Java核心技术,为应对面试提供全面指导。此外,还涉及反射、设计模式、JVM调优等进阶内容,助力开发者全面提升技能。代码示例可从提供的链接下载。
120 6
|
25天前
|
存储 缓存 安全
Java 集合篇面试题全面总结及答案解析
本文总结了Java集合框架的核心概念、常见集合类的特性与应用场景,以及开发中可能遇到的问题与解决方案。内容涵盖集合框架的基础接口(如Collection、Set、List、Map)、泛型的优点、线程安全集合类(如ConcurrentHashMap、CopyOnWriteArrayList)、常见集合类的区别(如ArrayList与LinkedList、HashMap与HashTable)等。此外,还详细介绍了如何实现LRU缓存、FIFO队列、优先级队列及栈等数据结构,并提供了相关代码示例。通过本文,读者可以全面掌握Java集合相关的面试知识点及其实际应用技巧。
50 1
|
27天前
|
算法 安全 Java
2025 校招必看:Java 开发面试核心知识点深度解析及最新笔面试题汇总
本文针对2025校招Java开发面试,系统梳理了Java基础、集合框架、多线程并发、JVM等核心知识点,并附带最新笔面试题。内容涵盖封装、继承、多态、异常处理、集合类使用、线程同步机制、JVM内存模型及垃圾回收算法等。同时深入探讨Spring、数据库(MySQL索引优化、Redis持久化)、分布式系统(CAP理论、分布式事务)等相关知识。通过理论结合实例解析,帮助考生全面掌握面试要点,提升实战能力,为成功拿下Offer奠定坚实基础。
118 2
|
28天前
|
存储 安全 Java
Java 基础知识面试题汇总 最全面的 Java 基础面试题整理
本文全面解析Java基础知识面试题,涵盖Java基础概念、面向对象编程、异常处理、集合框架等核心内容。通过实际应用场景,提供技术方案与应用实例,如JDK与JRE区别、==与equals()差异、String类特性、final与static关键字用法、多继承替代方案及接口与抽象类对比。帮助开发者夯实基础,高效备考,提升实战能力。附带完整代码示例,可供下载学习。
89 3