← 返回面试专题导航

📦 List / Set / Map 面试题

一页吃透三大核心接口的语义与差异

本页系统对比 List、Set、Map 的特性与典型实现,是集合框架面试的基础必备。

🎯 难度筛选

一、整体认知:List / Set / Map 有什么不同?

1. List、Set、Map 的核心语义分别是什么? 简单
完美答案

从语义到实现的完整理解:

核心语义对比: ┌──────────┬──────────────┬──────────────┬──────────────┐ │ 接口 │ List │ Set │ Map │ ├──────────┼──────────────┼──────────────┼──────────────┤ │ 是否有序│ 按插入顺序/索引│ 一般无序(可排序实现)│ Key 无序(可排序实现)│ │ 是否可重复│ 元素可重复 │ 元素不可重复 │ Key 不可重复,Value 可│ │ 访问方式│ 按索引访问 │ 迭代访问 │ 通过 Key 访问 Value │ │ 核心用途│ 有序集合 │ 去重集合 │ 键值映射 │ │ 典型场景│ 列表、队列 │ 去重、集合运算│ 缓存、字典、索引│ └──────────┴──────────────┴──────────────┴──────────────┘ 继承关系图: Collection ├── List (有序、可重复) │ ├── ArrayList (动态数组) │ ├── LinkedList (双向链表) │ ├── Vector (线程安全数组) │ └── CopyOnWriteArrayList (写时复制) │ ├── Set (无序、不可重复) │ ├── HashSet (哈希表) │ ├── LinkedHashSet (哈希表+链表) │ └── TreeSet (红黑树) │ └── Queue (队列) ├── LinkedList ├── PriorityQueue └── BlockingQueue 系列 Map (独立接口) ├── HashMap (哈希表) ├── LinkedHashMap (哈希表+链表) ├── TreeMap (红黑树) ├── Hashtable (线程安全) └── ConcurrentHashMap (并发哈希表)

深度解析:

  • List - 有序集合:
    • 保证元素的插入顺序,支持按索引访问
    • 允许存储重复元素,包括 null 值
    • 提供位置相关的操作:get(index)、add(index, element)
    • 典型应用:用户列表、购物车、任务队列
  • Set - 去重集合:
    • 不允许存储重复元素,基于 equals() 和 hashCode()
    • 最多包含一个 null 元素(HashSet)
    • 提供高效的 contains() 操作,时间复杂度 O(1)
    • 典型应用:标签系统、权限去重、数学集合运算
  • Map - 键值映射:
    • 存储键值对,Key 唯一,Value 可重复
    • 提供基于 Key 的快速查找,时间复杂度 O(1)
    • 支持 null Key(HashMap)和 null Value
    • 典型应用:缓存、配置、索引、计数器
// 语义差异的代码体现 public class CollectionSemantics { public static void demonstrateListSemantics() { List list = new ArrayList<>(); list.add("Java"); list.add("Python"); list.add("Java"); // 允许重复 list.add(1, "Go"); // 按索引插入 System.out.println(list.get(0)); // 按索引访问 System.out.println("List 保持插入顺序: " + list); } public static void demonstrateSetSemantics() { Set set = new HashSet<>(); set.add("Java"); set.add("Python"); set.add("Java"); // 自动去重 System.out.println("Set 自动去重: " + set); System.out.println("快速查找: " + set.contains("Java")); } public static void demonstrateMapSemantics() { Map map = new HashMap<>(); map.put("Java", 1); map.put("Python", 2); map.put("Java", 3); // 覆盖旧值 System.out.println("Map 键值映射: " + map); System.out.println("通过 Key 获取: " + map.get("Java")); } }
💡 面试加分点:能够从语义层面理解三个接口的设计初衷,而不仅仅是记忆 API。List 解决"有序序列"问题,Set 解决"去重"问题,Map 解决"映射"问题。
2. Java 集合框架中 List / Set / Map 的主要实现类有哪些? 中等
完美答案

从基础到高级的完整实现类体系:

实现类详细分析: List 接口实现: ┌─────────────────┬──────────────┬──────────────┬──────────────┐ │ 实现类 │ 数据结构 │ 时间复杂度 │ 适用场景 │ ├─────────────────┼──────────────┼──────────────┼──────────────┤ │ ArrayList │ 动态数组 │ 访问O(1)增删O(n)│ 随机访问频繁 │ │ LinkedList │ 双向链表 │ 访问O(n)增删O(1)│ 插入删除频繁 │ │ Vector │ 动态数组 │ 访问O(1)增删O(n)│ 需要线程安全 │ │ CopyOnWriteArrayList│ 写时复制数组 │ 读O(1)写O(n)│ 读多写少场景 │ └─────────────────┴──────────────┴──────────────┴──────────────┘ Set 接口实现: ┌─────────────────┬──────────────┬──────────────┬──────────────┐ │ 实现类 │ 数据结构 │ 特性 │ 适用场景 │ ├─────────────────┼──────────────┼──────────────┼──────────────┤ │ HashSet │ 哈希表 │ 无序、高性能 │ 快速去重 │ │ LinkedHashSet │ 哈希表+链表 │ 有序、性能好 │ 保持插入顺序 │ │ TreeSet │ 红黑树 │ 有序、可排序 │ 需要排序 │ └─────────────────┴──────────────┴──────────────┴──────────────┘ Map 接口实现: ┌─────────────────┬──────────────┬──────────────┬──────────────┐ │ 实现类 │ 数据结构 │ 线程安全 │ 适用场景 │ ├─────────────────┼──────────────┼──────────────┼──────────────┤ │ HashMap │ 哈希表 │ 非线程安全 │ 通用映射 │ │ LinkedHashMap │ 哈希表+链表 │ 非线程安全 │ LRU缓存 │ │ TreeMap │ 红黑树 │ 非线程安全 │ 有序映射 │ │ Hashtable │ 哈希表 │ 线程安全 │ 遗留类 │ │ConcurrentHashMap│ 分段哈希表 │ 线程安全 │ 高并发场景 │ └─────────────────┴──────────────┴──────────────┴──────────────┘
// 各实现类的性能特征对比 public class ImplementationComparison { // ArrayList - 最适合随机访问 public static void arrayListDemo() { List arrayList = new ArrayList<>(); // 优点:get(index) O(1),内存连续,缓存友好 // 缺点:扩容时需要复制数组,中间插入删除 O(n) arrayList.add("Element1"); arrayList.add("Element2"); String element = arrayList.get(0); // O(1) 访问 } // LinkedList - 适合频繁插入删除 public static void linkedListDemo() { List linkedList = new LinkedList<>(); // 优点:头尾插入删除 O(1),不需要预分配空间 // 缺点:随机访问 O(n),内存开销大,缓存不友好 linkedList.add("Element1"); ((LinkedList) linkedList).addFirst("Element0"); // O(1) } // HashSet - 最快的去重和查找 public static void hashSetDemo() { Set hashSet = new HashSet<>(); // 优点:add/contains/remove O(1),性能最佳 // 缺点:无序,不保证遍历顺序 hashSet.add("Java"); boolean exists = hashSet.contains("Java"); // O(1) } // LinkedHashMap - 保持顺序的高性能Set public static void linkedHashSetDemo() { Set linkedHashSet = new LinkedHashSet<>(); // 优点:保持插入顺序,性能接近 HashSet // 缺点:内存开销略大于 HashSet linkedHashSet.add("First"); linkedHashSet.add("Second"); // 遍历时按插入顺序输出 } // HashMap - 最通用的映射结构 public static void hashMapDemo() { Map hashMap = new HashMap<>(); // 优点:get/put O(1),支持 null key 和 value // 缺点:非线程安全,无序 hashMap.put("key", "value"); Object value = hashMap.get("key"); // O(1) } // TreeMap - 有序映射 public static void treeMapDemo() { Map treeMap = new TreeMap<>(); // 优点:按键排序,支持范围查询 // 缺点:性能 O(log n),需要实现 Comparable treeMap.put("C", "Value3"); treeMap.put("A", "Value1"); treeMap.put("B", "Value2"); // 自动按 key 排序:A, B, C } // ConcurrentHashMap - 高并发映射 public static void concurrentHashMapDemo() { Map concurrentMap = new ConcurrentHashMap<>(); // 优点:线程安全,高并发性能好 // 缺点:内存开销大,不支持 null key concurrentMap.put("key", "value"); // 多线程环境下安全操作 } }

选择策略:

  • 随机访问优先:ArrayList(读多写少)
  • 插入删除频繁:LinkedList(队列、栈场景)
  • 需要去重:HashSet(性能优先)
  • 需要有序:LinkedHashSet/TreeMap
  • 需要缓存:LinkedHashMap(LRU)
  • 高并发场景:ConcurrentHashMap
⚠️ 常见误区:不要盲目使用 Vector,它使用 synchronized 性能较差。并发场景优先选择 ConcurrentHashMap。

二、List 相关问题

3. List 的特点是什么?常见使用场景有哪些? 简单
  • 有序:元素有固定的顺序,可通过索引访问。
  • 可重复:允许存放重复元素。

典型场景:

  • 按顺序存放记录:如分页列表、历史记录。
  • 需要通过下标快速访问:如排名、排行榜。
List list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("A"); // 允许重复 System.out.println(list.get(0)); // A
4. List 的常见遍历方式有哪些?哪种更推荐? 中等
List list = List.of("A", "B", "C"); // 1. 传统 for for (int i = 0; i < list.size(); i++) { String s = list.get(i); } // 2. 增强 for(推荐) for (String s : list) { } // 3. Iterator for (Iterator it = list.iterator(); it.hasNext(); ) { String s = it.next(); } // 4. forEach + Lambda(推荐) list.forEach(System.out::println); // 5. Stream list.stream().filter(s -> !s.isEmpty()).forEach(System.out::println);

三、Set 相关问题

5. Set 如何保证元素不重复? 简单
  • HashSet:通过元素的 hashCode()equals() 判断是否相同。
  • TreeSet:通过 compareTo()Comparator 判断顺序与相等。
⚠️ 易错点:往 HashSet 放自定义对象时,如果不重写 equalshashCode,去重会失效。
6. HashSet、LinkedHashSet、TreeSet 的区别和使用场景? 中等
┌──────────────┬──────────────┬──────────────┐ │ 实现类 │ 底层结构 │ 特点 │ ├──────────────┼──────────────┼──────────────┤ │ HashSet │ HashMap │ 无序,去重 │ │ LinkedHashSet│ HashMap+链表 │ 按插入顺序 │ │ TreeSet │ 红黑树 │ 按排序/比较 │ └──────────────┴──────────────┴──────────────┘

四、Map 相关问题

7. Map 的常见遍历方式有哪些? 简单
Map map = new HashMap<>(); // 1. entrySet(推荐) for (Map.Entry e : map.entrySet()) { String k = e.getKey(); Integer v = e.getValue(); } // 2. keySet for (String k : map.keySet()) { Integer v = map.get(k); } // 3. values for (Integer v : map.values()) { } // 4. forEach(JDK 8+) map.forEach((k, v) -> System.out.println(k + "=" + v));
8. HashMap、LinkedHashMap、TreeMap 的适用场景大致如何区分? 中等
  • HashMap:绝大多数场景的默认选择,键无序。
  • LinkedHashMap:需要保持插入顺序或 LRU 缓存时使用。
  • TreeMap:需要对 Key 排序或做范围查询(子集视图)时使用。

五、选型与实践

9. 如何在 List / Set / Map 之间做选型? 中等
  • 需要按顺序存放、允许重复 → List。
  • 需要去重、只关心「是否存在」 → Set。
  • 需要通过 Key 查 Value → Map。
10. 在项目中你是如何结合 List、Set、Map 来建模业务数据的? 中等

可以准备一两个例子:例如「订单 + 订单项」、「标签去重」、「按用户 ID 建索引」等。

11. List / Set / Map 在并发环境下有什么需要注意的? 困难
  • 普通实现(ArrayList、HashSet、HashMap)都不是线程安全的。
  • 可以使用 Collections.synchronizedList / CopyOnWriteArrayList / ConcurrentHashMap 等并发实现。
12. 总结一下使用 List / Set / Map 时最容易犯的 3 个错误? 中等
  • 在 HashSet/HashMap 中放自定义对象却没正确重写 equals/hashCode
  • 在遍历时修改 List/Map 导致 ConcurrentModificationException
  • 在高并发下直接使用普通集合导致线程安全问题。

🏆 大厂面试真题专区

13. 【阿里巴巴】设计一个支持多种数据结构的统一容器,要求同时支持 List、Set、Map 操作 困难

核心考察点:接口设计、数据结构组合、性能优化、设计模式应用、泛型使用。

完美答案

从需求分析到完整实现的统一容器设计:

// 统一容器:同时支持List和Set语义 class UnifiedCollection { private List list = new ArrayList<>(); private Set set = new HashSet<>(); private Map indexMap = new HashMap<>(); public boolean add(T e) { if (set.contains(e)) return false; // Set语义 list.add(e); set.add(e); indexMap.put(e, list.size() - 1); return true; } public T get(int i) { return list.get(i); } // List语义 public boolean contains(Object o) { return set.contains(o); } // O(1)查找 public int fastIndexOf(Object o) { return indexMap.getOrDefault(o, -1); } } // 并发安全版本 class ConcurrentUnifiedCollection { private List list = new ArrayList<>(); private Set set = new HashSet<>(); private ReadWriteLock lock = new ReentrantReadWriteLock(); public boolean addUnique(T e) { lock.writeLock().lock(); try { if (set.contains(e)) return false; list.add(e); set.add(e); return true; } finally { lock.writeLock().unlock(); } } }

设计要点分析:

  • 接口兼容:同时实现 List 和 Set 接口,提供统一访问方式
  • 数据一致性:通过组合模式保证多个数据结构间的同步
  • 性能优化:使用索引映射实现 O(1) 的查找操作
  • 并发安全:读写锁保证多线程环境下的数据一致性
  • 扩展功能:提供计数、分组、转换等高级操作
💡 阿里面试加分点:能够分析不同实现方案的时空复杂度权衡,理解组合模式vs继承模式的优劣,并能设计出高性能的并发安全容器。
14. 【字节跳动】实现一个智能集合选择器,根据使用模式自动选择最优的数据结构 困难

核心考察点:性能分析、策略模式、动态优化、数据结构特征、机器学习应用。

完美答案

从性能监控到智能选择的完整解决方案:

// 智能集合选择器:根据使用模式自动优化 class SmartCollectionSelector { private Collection collection = new ArrayList<>(); private int addCount, getCount, containsCount; public boolean add(T e) { addCount++; boolean r = collection.add(e); if (shouldOptimize()) optimize(); return r; } private boolean shouldOptimize() { return (addCount + getCount + containsCount) % 100 == 0; } private void optimize() { double accessRatio = (double) getCount / (getCount + containsCount + 1); double modifyRatio = (double) addCount / (addCount + getCount + containsCount + 1); Collection newColl; if (accessRatio > 0.7) newColl = new ArrayList<>(); // 随机访问多 else if (modifyRatio > 0.6) newColl = new LinkedList<>(); // 修改多 else if (containsCount > getCount) newColl = new HashSet<>(); // 查找多 else return; newColl.addAll(collection); collection = newColl; } }

智能选择策略:

  • 操作模式分析:监控访问模式、修改频率、性能指标
  • 特征提取:提取集合大小、操作类型、时间特征等关键指标
  • 预测模型:使用机器学习算法预测最优数据结构
  • 动态迁移:在保证数据一致性的前提下自动迁移
  • 性能反馈:持续学习和优化选择策略
💡 字节面试加分点:能够设计出基于性能监控的智能选择系统,理解不同数据结构的性能特征,并能应用机器学习算法进行预测优化。
15. 【华为】设计一个分布式集合框架,支持跨节点的数据一致性和高可用性 困难

核心考察点:分布式系统设计、数据一致性、高可用架构、网络分区处理、性能优化。

完美答案

从单机到分布式的集合框架设计:

// 分布式集合:一致性哈希 + 数据分片 class DistributedSet { private ConsistentHash hash; private Map> nodeData = new ConcurrentHashMap<>(); private String localNode; public boolean add(T e) { String node = hash.getNode(e); if (node.equals(localNode)) return nodeData.get(localNode).add(e); else return rpc.sendAdd(node, e); // 远程调用 } public boolean contains(Object o) { String node = hash.getNode((T) o); if (node.equals(localNode)) return nodeData.get(localNode).contains(o); else return rpc.sendContains(node, o); } } // 一致性哈希 class ConsistentHash { private TreeMap ring = new TreeMap<>(); public String getNode(Object key) { int h = key.hashCode() & Integer.MAX_VALUE; Map.Entry e = ring.ceilingEntry(h); return e != null ? e.getValue() : ring.firstEntry().getValue(); } } // 高可用:主从复制 + 故障转移 class HADistributedSet { private String primary; private List replicas; private HealthChecker checker; public boolean add(T e) { if (!checker.isHealthy(primary)) primary = electNewPrimary(); boolean r = addToNode(primary, e); CompletableFuture.runAsync(() -> replicas.forEach(n -> addToNode(n, e))); // 异步复制 return r; } }

分布式设计要点:

  • 数据分片:使用一致性哈希实现数据均匀分布
  • 高可用性:主从复制、故障转移、自动恢复
  • 一致性保证:最终一致性模型,异步复制机制
  • 性能优化:批量操作、并行处理、本地缓存
  • 监控运维:健康检查、集群状态、性能指标
💡 华为面试加分点:能够设计出完整的分布式集合框架,理解一致性哈希、故障转移、数据复制等核心概念,并能处理网络分区和节点故障等复杂场景。