杰瑞科技汇

Java中List和Set的核心区别是什么?

核心区别总结(一张图看懂)

特性 List (列表) Set (集)
核心特点 有序、可重复 无序、不可重复
允许重复元素
元素顺序 有序,按插入顺序或排序规则存放 通常无序HashSet),或按排序规则存放(TreeSet
是否允许 null 元素 (通常允许多个) (但通常只允许一个,TreeSet 不允许)
常用实现类 ArrayList, LinkedList, Vector HashSet, LinkedHashSet, TreeSet
底层结构 动态数组 (ArrayList) / 双向链表 (LinkedList) 哈希表 (HashSet) / 哈希表+链表 (LinkedHashSet) / 红黑树 (TreeSet)
查询性能 ArrayList 快 (O(1)),LinkedList 慢 (O(n)) HashSet/LinkedHashSet 极快 (O(1)),TreeSet 较快 (O(log n))
插入性能 ArrayList 尾部快 (O(1)),中间/头部慢 (O(n))
LinkedList 头部/尾部快 (O(1)),中间慢 (O(n))
HashSet/LinkedHashSet 极快 (O(1))
TreeSet 较快 (O(log n))
适用场景 需要按索引访问、需要保留插入顺序、允许重复元素的场景 需要去重、不关心顺序(或需要特定排序)的场景

详细解释与对比

核心设计理念

  • List (列表)

    Java中List和Set的核心区别是什么?-图1
    (图片来源网络,侵删)
    • “袋子”:你可以把 List 想象成一个有序的袋子,你可以往里面放任何东西,可以放多个相同的东西,并且可以指定一个位置(索引)来取出某个特定的东西。
    • 核心是“位置”和“顺序”,它的每个元素都有一个精确的索引,从 0 开始。
  • Set (集)

    • “集合论中的集合”Set 严格遵循数学中集合的定义,即不允许有重复的元素,它像一个特殊的盒子,你放一个东西进去,如果已经有一样的东西了,它就不会再放进去。
    • 核心是“唯一性”,它不关心元素的插入顺序(除非是 LinkedHashSet),只关心元素是否唯一。

元素的唯一性与顺序

这是两者最根本的区别,我们来通过代码示例看看:

List 示例:

import java.util.ArrayList;
import java.util.List;
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Alice"); // 允许重复
names.add(1, "Charlie"); // 在索引1的位置插入
System.out.println(names); 
// 输出: [Alice, Charlie, Bob, Alice]
// 可以看到,有两个 "Alice","Charlie" 被插入到了指定位置。

Set 示例:

Java中List和Set的核心区别是什么?-图2
(图片来源网络,侵删)
import java.util.HashSet;
import java.util.Set;
Set<String> names = new HashSet<>();
names.add("Alice");
names.add("Bob");
names.add("Alice"); // 重复添加,这个操作无效
System.out.println(names); 
// 输出可能是: [Bob, Alice] 或 [Alice, Bob] (顺序不确定)
// 可以看到,只有一个 "Alice",且输出顺序不一定是插入顺序。

常用实现类详解

ListSet 都有多个实现类,了解它们的区别对于选择正确的工具至关重要。

List 的实现类

实现类 底层结构 特点 线程安全 适用场景
ArrayList 动态数组 查询快(随机访问 O(1)),增删慢(非尾部 O(n)) 最常用,需要频繁查询、不介意偶尔增删开销的场景。
LinkedList 双向链表 增删快(头部/尾部 O(1)),查询慢(O(n)) 需要频繁在头部或中间插入/删除元素的场景。
Vector 动态数组 功能和 ArrayList 一样,但线程安全 已经过时,性能差,现代开发中使用 CopyOnWriteArrayListCollections.synchronizedList() 替代。

Set 的实现类

实现类 底层结构 特点 线程安全 适用场景
HashSet 哈希表 不保证顺序,存取速度快(O(1)),不允许重复 最常用,只需要保证元素唯一性,不关心顺序的场景。
LinkedHashSet 哈希表 + 链表 保证插入顺序,存取速度快(O(1)),不允许重复 需要去重,同时保留插入顺序的场景。
TreeSet 红黑树 元素自然排序(或指定 Comparator),存取速度较快(O(log n)),不允许重复 需要对元素进行排序的场景。

性能对比

  • List (ArrayList):

    • get(int index): 极快,直接通过索引计算内存地址,时间复杂度为 O(1)
    • add(E e) (尾部): ,平均时间复杂度为 O(1),可能需要扩容。
    • add(int index, E e): ,需要移动 index 位置之后的所有元素,时间复杂度为 O(n)
    • remove(int index): ,同上,需要移动元素,时间复杂度为 O(n)
  • Set (HashSet):

    • add(E e), remove(Object o), contains(Object o): 极快,通过哈希码直接定位元素,平均时间复杂度为 O(1)
    • 注意:虽然 HashSet 的增删查很快,但它不能通过索引访问,如果要遍历查找某个元素,性能可能不如 ArrayList
  • Set (TreeSet):

    Java中List和Set的核心区别是什么?-图3
    (图片来源网络,侵删)
    • 所有操作(增、删、查)的时间复杂度都是 O(log n),因为底层是树结构,这比 HashSet 的 O(1) 慢,但保证了有序性。

null 值的处理

  • List: 通常可以包含多个 null 值。
    List<String> list = new ArrayList<>();
    list.add(null);
    list.add(null);
    System.out.println(list); // [null, null]
  • Set:
    • HashSet: 可以包含一个 null 值。
      Set<String> set = new HashSet<>();
      set.add(null);
      System.out.println(set); // [null]
    • TreeSet: 不允许包含 null 值,如果尝试添加,会抛出 NullPointerException
      Set<String> set = new TreeSet<>();
      set.add(null); // 抛出 NullPointerException

如何选择?(决策指南)

问自己以下几个问题来决定使用 List 还是 Set

  1. 我的数据需要重复吗?

    • -> 必须使用 List
    • -> 继续往下问。
  2. 我需要保留数据的插入顺序吗?

    • -> 使用 LinkedHashSet
    • -> 继续往下问。
  3. 我需要对数据进行排序吗?

    • -> 使用 TreeSet
    • -> 使用 HashSet (这是最常用的 Set)。
  4. 我需要根据索引(位置)来访问、插入或删除元素吗?

    • -> 必须使用 List,这是 List 的核心功能,Set 完全不支持。

总结流程图:

需要存储一组对象?
    |
    +-- 数据允许重复? ---是---> 使用 List
    |                         |
    |                         +-- 需要随机访问/索引操作? ---> ArrayList
    |                         |
    |                         +-- 需要频繁在头/尾/中间增删? ---> LinkedList
    |
    +-- 数据不允许重复? ---否--->
                                  |
                                  +-- 需要按特定规则排序? ---> TreeSet
                                  |
                                  +-- 不需要排序? ----------+
                                                          |
                                                          +-- 需要保留插入顺序? ---> LinkedHashSet
                                                          |
                                                          +-- 不关心顺序? ---------> HashSet
分享:
扫描分享到社交APP
上一篇
下一篇