← 返回题目列表

Java 集合框架的整体结构是怎样的?

高频 简单 第 3 / 30 题 更新于 2026/07/25
集合框架CollectionMapListSet

简化版

Java 集合分两大体系:Collection(单个元素的集合)和 Map(键值对)。Collection 下面三支——List(有序可重复)、Set(无序不重复)、Queue(队列)。常用实现:List 有 ArrayList/LinkedList,Set 有 HashSet/TreeSet,Map 有 HashMap/TreeMap,Queue 有 ArrayDeque。选型看你要有序还是去重、要不要排序、并发不并发

详细版

整体结构

Iterable
  └─ Collection
       ├─ List   (有序、可重复、有索引)
       │    ├─ ArrayList     动态数组,随机访问快
       │    └─ LinkedList    双向链表,也实现了 Deque
       ├─ Set    (不重复)
       │    ├─ HashSet       无序,基于 HashMap
       │    ├─ LinkedHashSet 保持插入顺序
       │    └─ TreeSet       自动排序,基于红黑树
       └─ Queue  (队列/双端队列)
            ├─ ArrayDeque    数组实现的双端队列(做栈/队列都推荐)
            └─ PriorityQueue 优先级队列(堆)

Map  (键值对,不属于 Collection)
  ├─ HashMap        无序,最常用
  ├─ LinkedHashMap  保持插入/访问顺序(可做 LRU)
  ├─ TreeMap        按 key 排序,红黑树
  └─ Hashtable      遗留的线程安全 Map(已淘汰)

三大接口特征

  • List:有序(按插入顺序)、可重复、有下标。要「一串按顺序、可重复的元素」用它。
  • Set:不可重复。要「去重」用它,靠 hashCode+equals 判重。
  • Map:key→value 映射,key 不重复。要「按 key 查 value」用它。Map 不是 Collection 的子接口,它是独立体系。

完整版教学

一、先建立两条主线:Collection 和 Map

集合框架看着庞大,其实就两棵树:

  • Collection(继承自 Iterable,所以能 for-each):装单个元素,下分 List / Set / Queue。
  • Map:装键值对,独立于 Collection。

初学者常问「Map 为什么不在 Collection 里」——因为 Collection 的抽象是「一组元素」,而 Map 是「一组映射关系」,元素形态不同(一个是 element,一个是 key-value 对),强行归一反而别扭,所以 JDK 让它独立成体系。

二、List / Set / Queue 各自解决什么

  • List——要顺序、要重复、要按位置访问:日志列表、购物车、查询结果。首选 ArrayList(随机访问快、缓存友好),频繁两端操作用 ArrayDeque
  • Set——要去重:标签、参与用户 id、去重统计。HashSet 最快(无序),要保持插入序用 LinkedHashSet,要排序用 TreeSet
  • Queue/Deque——要先进先出或双端操作:任务队列、BFS。用 ArrayDeque(做栈和队列都比 Stack/LinkedList 好),要按优先级出队用 PriorityQueue

记忆点:有序可重复选 List,去重选 Set,排队选 Queue,键值映射选 Map。

三、Hash / Linked / Tree 三个前缀的含义

集合类名前缀是有规律的,认准就能秒懂特性:

  • Hash 前缀(HashMap/HashSet):基于哈希表,无序、增删查 O(1)、最快。
  • Linked 前缀(LinkedHashMap/LinkedHashSet):哈希表 + 链表,保持插入/访问顺序
  • Tree 前缀(TreeMap/TreeSet):基于红黑树,自动排序、增删查 O(log n)、能做范围查询(如 subMapheadSet)。

需求映射:不在乎顺序求最快 → Hash;要保留插入顺序 → Linked;要排序或范围查 → Tree。

四、线程安全怎么办

上面这些默认都不是线程安全的。并发场景有几种做法:

  • 首选并发容器ConcurrentHashMap(并发 Map)、CopyOnWriteArrayList(读多写少的 List)、ConcurrentLinkedQueue
  • 遗留同步类VectorHashtable —— 全表锁,性能差,不推荐
  • 包装同步Collections.synchronizedList/Map(...) —— 也是全表锁,一般不如并发容器。

五、接口、实现与视图要分清

变量尽量声明为接口,例如 List<String> names = new ArrayList<>(),让调用方依赖 List 契约而不是数组实现。这样以后换成不可变列表或并发列表时,使用方不必跟着改;但若代码确实需要 ensureCapacity 这类实现专属能力,就应显式承认自己依赖 ArrayList。

集合 API 还经常返回“视图”而不是独立副本。map.keySet()map.values()map.entrySet() 与原 Map 相连,从视图删除元素会改变原 Map;list.subList(from, to) 也共享底层结构。若需要隔离快照,应再构造一份新集合。

Map<String, Integer> scores = new HashMap<>();
scores.put("A", 90);
Set<String> keys = scores.keySet();
keys.remove("A");                 // scores 也删除了 A

List<Integer> snapshot = new ArrayList<>(source); // 独立容器快照
API 结果是否独立修改影响
map.keySet()否,Map 视图删除会反映到 Map
list.subList(a,b)否,List 视图结构修改可能互相影响
Arrays.asList(array)固定大小视图可 set,不能 add/remove,元素与数组联动
new ArrayList<>(source)新容器容器结构独立,元素对象仍共享
List.copyOf(source)不可变副本/复用不允许修改,且拒绝 null 元素

六、常见误区与追问

  • 误区:Map 属于 Collection。 它是独立的键值映射体系,只能通过 keySet、values、entrySet 暴露集合视图。
  • 误区:Hash 前缀意味着遍历顺序固定。 哈希实现不承诺顺序,顺序需求应选择 Linked 或 Tree 实现。
  • 误区:Arrays.asList 是普通 ArrayList。 它是数组支持的固定大小列表,增删会抛 UnsupportedOperationException
  • 追问:List 的“有序”是否等于“自动排序”? 不是,有序指保留位置/迭代顺序;自动按比较规则排序的是 TreeSet、TreeMap 等。
  • 追问:Queue 的 add/remove/elementoffer/poll/peek 有何区别? 前一组失败时抛异常,后一组用返回值表示容量不足或队列为空。
  • 追问:普通同步包装是否等于复合操作安全? 不是,遍历或“先检查再修改”仍需按文档在同一锁上同步。

选型心法:先定语义接口,再定顺序、复杂度和并发实现,最后确认拿到的是独立集合还是共享视图。

泛型约束也属于集合契约。List<? extends Number> 适合读取 Number,却不能安全添加具体数字;List<? super Integer> 可以写入 Integer,读取时只能按 Object 看待,这就是常说的 PECS:生产者 extends,消费者 super。

static double sum(List<? extends Number> values) { /* 只读 */ }
static void addDefaults(List<? super Integer> out) { out.add(0); }

它解决的是不同泛型集合之间的类型协作,不会改变底层容器的顺序、复杂度或线程安全特征。

七、加强记忆

集合框架先分 Collection 与 Map 两条主线;Collection 再按 List 的位置语义、Set 的唯一性和 Queue 的排队语义选择。实现名前缀提示底层取舍:Hash 求平均 O(1),Linked 保持顺序,Tree 提供排序与范围查询。面向接口声明变量,并发时选专用容器,还要警惕 keySet、subList、Arrays.asList 等共享视图,才能从“会背类名”进阶到正确使用集合契约。