一文搞懂 Java 集合,再也不用担心集合数据类型的操作了

2023年 7月 14日 117.6k 0

一、简介

集合的定义和作用

Java集合是用于存储和操作一组对象的数据结构。它提供了一组接口和类,用于处理不同类型的集合数据,如列表、集、映射等。

Java集合的主要作用是:

  • 存储对象:Java集合可以存储不同类型的对象,并提供了方便的方法来添加、删除和访问集合中的元素。

  • 管理数据:集合提供了多种数据结构,如列表、集、映射等,可以根据不同的需求选择合适的数据结构来管理数据。比如,列表可以按照插入顺序存储数据,集可以保证元素的唯一性,映射可以通过键值对来存储和访问数据等。

  • 提供算法和操作:Java集合提供了一系列算法和操作,如排序、查找、过滤等,可以方便地对集合中的元素进行处理和操作。

  • 提高效率:集合类中的数据结构和算法经过优化,可以提高程序的执行效率。比如,使用HashSet来存储大量数据时,可以通过散列算法快速查找元素,而不需要遍历整个集合。

  • 集合框架的基本概念

    Java集合框架是Java编程语言提供的一组类和接口,用于存储、操作和处理数据集合。它提供了各种类型的数据结构,如列表、集合、映射等,以及用于操作和处理这些数据结构的算法和工具。Java集合框架的设计目标是提供高性能、可靠性和可扩展性的数据结构和算法。

    Java集合框架的基本概念如下:

  • 接口(Interface):Java集合框架中定义了许多接口,如List、Set、Map等。接口定义了一组操作集合的方法,是集合框架的核心部分。

  • 类(Class):Java集合框架提供了实现接口的具体类,如ArrayList、HashSet、HashMap等。这些类实现了接口定义的方法,提供了具体的数据结构和算法。

  • 列表(List):列表是有序的集合,可以包含重复的元素。Java集合框架提供了多个列表的实现类,如ArrayList、LinkedList等。

  • 集合(Set):集合是无序的、不重复的元素的集合。Java集合框架提供了多个集合的实现类,如HashSet、TreeSet等。

  • 映射(Map):映射是一种键值对的集合,每个键对应一个值。Java集合框架提供了多个映射的实现类,如HashMap、TreeMap等。

  • 队列(Queue):队列是一种先进先出(FIFO)的数据结构。Java集合框架提供了多个队列的实现类,如LinkedList、PriorityQueue等。

  • 迭代器(Iterator):迭代器用于遍历集合中的元素,提供了统一的访问集合元素的方式。通过迭代器,可以依次访问集合中的每个元素。

  • 泛型(Generics):Java集合框架使用泛型来增加类型安全性和代码的可读性。通过泛型,可以指定集合中存储的元素类型。

  • 算法(Algorithm):Java集合框架提供了许多算法和工具类,用于对集合进行排序、搜索、过滤等操作。这些算法和工具类可以方便地操作集合中的元素。

  • Java集合框架的使用非常广泛,它提供了丰富的数据结构和算法,可以满足各种不同的编程需求。

    集合框架的接口和实现类

    Java集合框架提供了一些核心接口和相关的实现类,用于存储和操作不同类型的集合数据。

  • Collection 接口:

    • List 接口:有序、可重复的集合,可以通过索引访问元素。常见的实现类有 ArrayList、LinkedList、Vector。
    • Set 接口:无序、不可重复的集合,不允许存储相同元素。常见的实现类有 HashSet、TreeSet、LinkedHashSet。
  • Map 接口:

    • HashMap 类:无序的键值对集合,根据键来存储和访问。常用于快速查找和存储关联数据。
    • TreeMap 类:有序的键值对集合,按照键的自然顺序或特定比较器定义的顺序来存储和访问。
    • LinkedHashMap 类:有序的键值对集合,按照插入顺序或访问顺序来存储和访问。
  • Queue 接口:

    • LinkedList 类:实现了队列的数据结构,支持先进先出(FIFO)的元素访问方式。
  • 栈(Stack)接口:

    • Stack 类:继承自 Vector 类,实现了后进先出(LIFO)的元素访问方式。
  • 接口工具类:

    • Collections 类:提供了一组静态方法,用于对集合对象进行常见操作,如排序、查找、反转等。
  • 除了上述接口和实现类之外,Java集合框架还提供了其他接口和实现类,用于特定的集合需求。例如:

    • Deque 接口:双端队列,可以在两端插入和删除元素。
    • PriorityQueue 类:优先队列,按照元素的顺序进行访问,优先级高的元素先被访问。
    • BitSet 类:位集合,用于存储和操作位信息。

    集合框架的继承结构

  • Collection 接口:是所有集合的基本接口,定义了对集合元素的基本操作。它继承自 Iterable 接口,表示支持迭代访问。

  • List 接口:继承自 Collection 接口,表示有序、可重复的集合。List 接口提供了按索引访问和操作集合元素的方法。常见的实现类有 ArrayList、LinkedList、Vector。

  • Set 接口:继承自 Collection 接口,表示无序、不可重复的集合。Set 接口不允许存储相同的元素。常见的实现类有 HashSet、TreeSet、LinkedHashSet。

  • Queue 接口:继承自 Collection 接口,表示一种特殊的集合,用于实现队列(先进先出)的数据结构。Queue 接口提供了添加、删除和检查元素的方法。常见的实现类有 LinkedList、PriorityQueue。

  • Deque 接口:继承自 Queue 接口,表示双端队列,可以在两端插入和删除元素。Deque 接口提供了在队首、队尾进行插入和删除操作的方法。常见的实现类有 LinkedList、ArrayDeque。

  • Map 接口:表示键值对的集合,每个键都是唯一的。Map 接口定义了对键值对进行操作的方法。常见的实现类有 HashMap、TreeMap、LinkedHashMap。

  • 除了上述基本接口之外,还有一些继承于上述接口的子接口和实现类,用于扩展集合框架的功能。

    二、List集合

    List集合的接口和实现类

    List 是 Java 集合框架中的接口之一,它继承自 Collection 接口。List 表示有序、可重复的集合,每个元素都有一个对应的索引,可以通过索引访问和操作元素。

  • List 接口:

    • 方法:List 接口继承自 Collection 接口,除了 Collection 接口的方法外,额外提供了一些特有的方法,如根据索引操作元素、获取子列表等。
    • 实现类:List 接口的主要实现类包括 ArrayList、LinkedList 和 Vector。
  • ArrayList 类:

    • 实现了 List 接口,底层使用数组来存储元素,支持动态扩容。
    • 优点:随机访问元素快速,效率高;适用于频繁的随机访问和遍历操作。
    • 缺点:插入和删除元素的效率相对较低,需要移动大量元素。
    • 线程不安全:ArrayList 不是线程安全的,若需多线程操作,建议使用线程安全的实现类 Vector 或 Collections 工具类的 synchronizedList 方法进行包装。
  • LinkedList 类:

    • 实现了 List 和 Deque 接口,底层使用双向链表来存储元素。
    • 优点:插入和删除元素的效率高,不需要移动大量元素;支持高效的首尾操作。
    • 缺点:随机访问元素效率相对较低。
    • 在需要频繁插入和删除元素的场景中使用 LinkedList 效果更好。
  • Vector 类:

    • 实现了 List 接口,底层也使用数组来存储元素,类似于 ArrayList。
    • 优点:线程安全,可以在多线程环境下使用;支持动态扩容。
    • 缺点:相比 ArrayList,性能稍差。
    • 注意:Vector 是较早的集合实现类,在现代开发中,一般推荐使用 ArrayList 或 LinkedList。
  • ArrayList和LinkedList的区别和优缺点

    ArrayList 和 LinkedList 是 List 接口的两个常见实现类,它们在底层数据结构和性能特点上有所不同。

    ArrayList:

    • 底层数据结构:ArrayList 使用数组来实现,内部维护一个 Object 类型的数组,默认初始容量为 10。
    • 优点:
      • 随机访问元素快速:由于是基于数组实现,通过索引可以直接访问元素,查找和读取效率高。
      • 空间效率高:相对于链表结构,数组具有连续的内存空间,不需要额外的存储空间。
      • 迭代操作效率高:迭代操作和遍历效率高,适用于频繁的随机访问和遍历操作。
    • 缺点:
      • 插入和删除元素的效率较低:插入和删除元素需要移动大量元素,特别是在数组中间或开头进行操作时。
      • 动态扩容开销:当元素超过当前容量时,需要进行动态扩容,可能导致性能下降。

    LinkedList:

    • 底层数据结构:LinkedList 使用双向链表来实现,每个节点包含前后指针以及元素值。
    • 优点:
      • 插入和删除元素的效率高:由于是链表结构,插入和删除元素只需调整相邻节点的指针,效率较高。
      • 支持高效的首尾操作:对头部和尾部元素的插入和删除操作效率极高。
    • 缺点:
      • 随机访问元素效率相对较低:要查找特定索引的元素需要从头或尾开始遍历链表,时间复杂度为 O(n)。
      • 需要更多存储空间:相对于数组,链表需要额外的指针来维护节点之间的连接关系。

    选择 ArrayList 还是 LinkedList 取决于具体的应用场景和操作需求:

    • 如果需要频繁进行随机访问、读取和遍历操作,而插入和删除操作较少,可以选择 ArrayList,它具有更高的访问和读取性能。
    • 如果需要频繁进行插入和删除操作,尤其是在链表的头部或尾部进行操作,则选择 LinkedList 更加合适,它具有更高的插入和删除性能。
    • 如果涉及到多线程环境,建议使用线程安全的 Vector 或利用 Collections 工具类的 synchronizedList 方法包装 ArrayList/LinkedList。

    List集合的常用操作和方法

  • 添加元素操作:

    • boolean add(E element): 将指定元素添加到列表的末尾。
    • void add(int index, E element): 在指定索引位置插入指定元素。
    • boolean addAll(Collection c): 从列表中删除与指定集合中相同的所有元素。
    • void clear(): 清空列表中的所有元素。
  • 获取元素操作:

    • E get(int index): 返回指定索引位置的元素。
    • int indexOf(Object o): 返回指定元素第一次出现的索引。
    • int lastIndexOf(Object o): 返回指定元素最后一次出现的索引。
    • List subList(int fromIndex, int toIndex): 返回列表中指定范围的子列表。
  • 修改元素操作:

    • E set(int index, E element): 将指定索引位置的元素替换为指定元素。
  • 判断元素是否存在:

    • boolean contains(Object o): 判断列表是否包含指定元素。
    • boolean containsAll(Collection c): 判断列表是否包含指定集合中的所有元素。
  • 判断集合大小和空判断:

    • int size(): 返回列表中的元素个数。
    • boolean isEmpty(): 判断列表是否为空。
  • 遍历操作:

    • 使用 for-each 循环遍历列表中的元素。
    • 使用迭代器(Iterator)遍历列表中的元素。
  • List集合的遍历方式

  • 使用 for 循环遍历:

    for (int i = 0; i < list.size(); i++) {
        E element = list.get(i);
        // 对元素进行操作
    }
    

    通过索引可以逐个访问列表中的元素,并进行相应的操作。

  • 使用增强型 for 循环遍历(foreach 循环):

    for (E element : list) {
        // 对元素进行操作
    }
    

    增强型 for 循环可以直接遍历列表中的元素,无需使用索引。

  • 使用迭代器(Iterator)遍历:

    Iterator iterator = list.iterator();
    while (iterator.hasNext()) {
        E element = iterator.next();
        // 对元素进行操作
    }
    

    通过调用 iterator() 方法获取迭代器对象,利用 hasNext() 方法判断是否还有下一个元素,通过 next() 方法获取下一个元素进行操作。

  • 使用 Stream API 遍历(Java 8 及以上):

    list.stream().forEach(element -> {
        // 对元素进行操作
    });
    

    使用 Stream API 的 stream() 方法将列表转换为流,然后利用 forEach() 方法对流中的每个元素进行操作。

  • 三、Set集合

    Set集合的接口和实现类

    Set 接口是 Java 集合框架的一部分,它扩展了 Collection 接口。Set 表示一个不允许包含重复元素的集合。

    Set 接口的特点主要包括:

  • 不允许重复:Set 中的元素是唯一的,如果尝试向 Set 中添加重复元素,则添加操作会失败。
  • 无序性:Set 中的元素没有固定的顺序。不同的实现类可能以不同的方式存储和迭代元素,因此不能保证元素的存取顺序和插入顺序一致。
  • 允许存储 null 元素:Set 中可以存储一个 null 元素(某些实现类除外)。
  • 常见的 Set 实现类包括:

    • HashSet:基于哈希表实现。HashSet 使用 HashMap 存储元素,并使用元素的哈希码进行索引,因此添加、删除和查找操作的时间复杂度为 O(1)。它是最常用的 Set 实现类,但不保证元素的顺序。
    • LinkedHashSet:继承自 HashSet,基于哈希表和链表实现。它通过链表维护元素的插入顺序,因此可以有序地遍历元素。性能略低于 HashSet,但仍然比较高效。
    • TreeSet:基于红黑树实现。TreeSet 中的元素会按照自然顺序(或者通过 Comparator 接口定义的顺序)进行排序。因此,TreeSet 可以提供一系列有序集合操作。
    • EnumSet:专门用于存储枚举类型的 Set 集合。由于枚举类型的取值是有限且固定的,EnumSet 内部使用位向量实现,具有极高的效率。

    需要注意的是,Set 接口本身不能直接实例化,必须使用具体的实现类来创建对象。示例代码如下:

    Set set = new HashSet();  // 创建一个 HashSet 对象
    

    根据需求和特定场景,选择适合的 Set 实现类。HashSet 是最常用的 Set 实现类,适用于大多数情况。如果需要保持插入顺序或按照自然顺序排序,可以选择 LinkedHashSet 或 TreeSet。当需要存储枚举类型的元素时,EnumSet 是最佳选择,因为它是高效的。

    HashSet和TreeSet的区别和优缺点

  • 内部实现机制不同:

    • HashSet 使用哈希表(HashMap 实现)存储元素,根据元素的哈希码进行索引。因此,HashSet 具有快速的插入、删除和查找操作,时间复杂度为 O(1)。
    • TreeSet 则是基于红黑树(TreeMap 实现)存储元素,它会按照元素的自然顺序(或者通过 Comparator 接口定义的顺序)进行排序。因此,TreeSet 提供了一些有序集合操作,如范围查找和排序等。
  • 元素顺序:

    • HashSet 中的元素没有特定的顺序,不保证元素的存取顺序和插入顺序一致。因为 HashSet 使用哈希表存储数据,元素在哈希表中的位置是根据元素的哈希码决定的,而不是插入的顺序。
    • TreeSet 中的元素是有序的,根据元素的自然顺序进行排序。或者通过传入的 Comparator 接口来定义排序规则。
  • 性能:

    • HashSet 的插入、删除和查找操作的平均时间复杂度为 O(1)。但是,当哈希表发生冲突时,性能会有所下降,并且在极端情况下,最坏时间复杂度可能达到 O(n)。
    • TreeSet 的插入、删除和查找操作的时间复杂度为 O(log n),其中 n 是 TreeSet 中元素的数量。由于红黑树的自平衡性质,它可以保持较为稳定的性能。
  • 允许存储 null 元素的情况:

    • HashSet 允许存储一个 null 元素(只能存储一个),因为哈希表中使用特殊的方式处理 null 值。
    • TreeSet 不允许存储 null 元素,因为它需要对元素进行排序,而 null 值无法进行比较。
  • 综上所述,HashSet 适用于需要快速插入、删除和查找的场景,并不关心元素的顺序。而 TreeSet 适用于需要有序集合操作的场景,例如范围查找和排序等,但由于红黑树的特性,其性能相对较低。同时,请注意 HashSet 和 TreeSet 都是线程不安全的,如果在多线程环境下使用,需要通过 Collections 工具类提供的 synchronizedSet 方法进行包装。

    Set集合的常用操作和方法

  • 添加元素:

    • boolean add(E element): 将指定元素添加到 Set 中。如果元素已经存在于 Set 中,则添加操作失败并返回 false。
    • 示例代码:
      Set set = new HashSet();
      set.add("apple");
      set.add("banana");
      
  • 移除元素:

    • boolean remove(Object o): 从 Set 中移除指定元素。如果元素存在于 Set 中,则移除操作成功并返回 true。
    • 示例代码:
      set.remove("apple");
      
  • 检查元素是否存在:

    • boolean contains(Object o): 判断 Set 中是否包含指定元素。如果元素存在于 Set 中,则返回 true。
    • 示例代码:
      boolean contains = set.contains("banana");
      
  • 获取元素数量:

    • int size(): 返回 Set 中元素的数量。
    • 示例代码:
      int size = set.size();
      
  • 判断是否为空集:

    • boolean isEmpty(): 判断 Set 是否为空集。如果 Set 不包含任何元素,则返回 true。
    • 示例代码:
      boolean isEmpty = set.isEmpty();
      
  • 清空集合:

    • void clear(): 清空 Set 中的所有元素,使其变为空集。
    • 示例代码:
      set.clear();
      
  • 迭代器:

    • Iterator iterator(): 返回一个迭代器,用于遍历 Set 中的元素。
    • 示例代码:
      Iterator iterator = set.iterator();
      while (iterator.hasNext()) {
          String element = iterator.next();
          // 处理元素
      }
      
  • 需要注意的是,Set 集合没有提供按索引访问元素的方法,因为在 Set 中元素没有固定的顺序。

    此外,Set 接口还继承了 Collection 接口中定义的一些方法,如addAll()removeAll()retainAll()containsAll() 等,用于集合之间的操作。

    Set集合的遍历方式

  • 迭代器遍历:
    迭代器是一种用于遍历集合元素的通用方式,在遍历过程中允许修改集合。Set 集合的迭代器通过调用iterator()方法获取,然后使用hasNext()next()方法遍历元素。

    示例代码:

    Set set = new HashSet();
    // 添加元素...
    
    Iterator iterator = set.iterator();
    while (iterator.hasNext()) {
        String element = iterator.next();
        // 处理元素
    }
    
  • 增强型 for 循环遍历:
    增强型 for 循环是遍历集合元素的简化方式,适用于遍历整个集合的情况。它不需要显式地使用迭代器,只需指定一个变量来接收每个元素。

    示例代码:

    Set set = new HashSet();
    // 添加元素...
    
    for (String element : set) {
        // 处理元素
    }
    
  • forEach() 方法遍历(Java 8+):
    Java 8 引入的 forEach() 方法提供了一种更简洁的遍历方式,可以使用 Lambda 表达式来处理每个元素。

    示例代码:

    Set set = new HashSet();
    // 添加元素...
    
    set.forEach(element -> {
        // 处理元素
    });
    
  • 无论使用哪种遍历方式,都可以用于遍历 Set 集合中的所有元素。需要注意的是,在遍历过程中不要修改集合的内容,否则可能导致遍历失败或出现异常。另外,Set 集合中的元素没有固定的顺序,因此无法保证遍历的顺序与添加的顺序相同。

    四、Map集合

    Map集合的接口和实现类

    Map 是 Java 集合框架中用于存储键值对的接口,它提供了一种将键映射到值的方式。在 Map 中,键是唯一的,而值可以重复。

    常用的 Map 实现类:

  • HashMap:
    HashMap 是最常用的 Map 实现类,它基于哈希表实现,允许使用 null 作为键和值。HashMap 不保证遍历顺序,性能较高。

  • TreeMap:
    TreeMap 基于红黑树实现,按键的自然顺序或自定义比较器的顺序对键进行排序。TreeMap 不允许使用 null 作为键,但允许使用 null 作为值。

  • LinkedHashMap:
    LinkedHashMap 继承自 HashMap,底层使用哈希表和双向链表实现。它保持了元素插入的顺序,并可以使用访问顺序进行迭代。

  • HashTable:
    HashTable 是早期的 Map 实现类,基于哈希表实现。它不允许使用 null 作为键或值,线程安全但性能较差,通常不推荐使用。

  • HashMap和TreeMap的区别和优缺点

    HashMap:

    • 特点:

      • 基于哈希表实现,通过键的哈希值进行快速查找和存储。
      • 不保证遍历顺序,即元素的存储顺序与插入顺序无关。
      • 允许使用 null 作为键和值。
      • 非线程安全。
    • 优点:

      • 在大多数情况下,HashMap 的操作时间复杂度为 O(1),平均性能较高。
      • 适用于不需要关心元素顺序的场景,例如快速查找、存储、删除元素。
      • 内存占用相对较小。
    • 缺点:

      • 不保证元素的遍历顺序,所以无法按照插入顺序或者键的顺序进行迭代。
      • HashMap 不是线程安全的,如果需要在多线程环境中使用,需要进行外部同步。

    TreeMap:

    • 特点:

      • 基于红黑树实现,通过键的自然顺序或自定义比较器进行排序。
      • 按键的有序状态维护元素,支持按键范围查询和遍历。
      • 不允许使用 null 作为键,但允许使用 null 作为值。
      • 非线程安全。
    • 优点:

      • TreeMap 保证元素的有序性,可以按照键的自然顺序或者自定义比较器的顺序进行迭代。
      • 适用于需要按照键的顺序进行遍历、范围查询的场景。
      • 提供了一些附加的方法,如 firstKey()、lastKey() 等。
    • 缺点:

      • 插入、删除和查找操作的时间复杂度为 O(logN),性能略低于 HashMap。
      • 内存占用相对较大。

    选择使用 HashMap 还是 TreeMap 取决于实际应用场景:

    • 如果对于元素的插入、删除和查找操作要求性能较高,并且不关心元素的顺序,可以选择使用 HashMap。
    • 如果需要按照键的顺序进行遍历、范围查询等操作,或者需要根据键的自然顺序进行排序,可以选择使用 TreeMap。
    • 在多线程环境下,需要考虑线程安全性,可以使用 ConcurrentHashMap 来替代 HashMap 或 ConcurrentSkipListMap 来替代 TreeMap。

    Map集合的常用操作和方法

  • 添加和修改元素:

    • V put(K key, V value): 将指定的键值对添加到 Map 中,如果键已存在,则会覆盖原有的值并返回旧值。
    • void putAll(Map list): 反转指定 List 中元素的顺序。
  • 查找和替换操作:

    • binarySearch(List coll): 判断指定 Collection 是否为空。
    • reverseOrder(): 返回一个比较器,用于实现降序排序。
    • shuffle(List list): 随机打乱指定 List 中元素的顺序。
  • Collections类的排序方法

    Collections 类提供了多种排序方法来对集合进行排序。这些排序方法可以用于对 List、Set 和数组等集合进行排序。

  • sort(List list):

    • 方法签名:public static
  • 相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论