Java 集合框架

本文首发于公众号:JavaArchJourney

Java集合框架

Java 集合框架是提供了一套设计优良、易于使用和扩展的接口和类,用于存储和操作一组对象。集合框架位于java.util包中,它提供了丰富的功能来处理数据集合,包括列表( List )、集合( Set )、队列( Queue )、映射( Map )等。

  • Collection:这是集合层次结构中的根接口。它包含了用于添加、移除和搜索元素的方法。
  • List:继承自 Collection 接口,是一个有序的集合,允许包含重复元素。支持通过索引来访问元素。
    • 实现类包括 ArrayListLinkedListVector 等。
  • Set:也是一个继承自 Collection 接口的集合类型,但不允许包含重复元素。
    • 常见实现类有 HashSetLinkedHashSetTreeSet
  • Queue:通常用于实现 FIFO (先进先出)的数据结构,但也有其他变种如优先队列。
    • 优先级队列 PriorityQueue 是一个典型的实现。
  • Map:不是 Collection 接口的子接口,它存储键值对。Map 不能包含重复的键;每个键最多只能映射到一个值。
    • 常见实现类有 HashMapLinkedHashMapTreeMap

Collection接口

Collection接口关系:

List接口实现类介绍

List 表示一个有序的、允许重复元素的集合,并且可以通过索引(位置)来访问和操作其中的元素。

常用方法:

  • add(E element):在列表末尾添加一个元素。
  • add(int index, E element):在指定索引处插入一个元素。
  • get(int index):返回指定索引处的元素。
  • set(int index, E element):替换指定索引处的元素。
  • remove(int index):删除指定索引处的元素。
  • size():返回列表中的元素个数。
  • indexOf(Object o):返回指定元素首次出现的索引,不存在则返回 -1。
  • lastIndexOf(Object o):返回指定元素最后一次出现的索引。
  • subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 的子列表。

List接口的常见实现类:

  • ArrayList:基于动态数组实现。更多阅读:ArrayList源码分析。
  • LinkedList:基于双向链表实现。更多阅读:LinkedList源码分析。
  • ArrayList LinkedList对比如下:
特性/类别 ArrayList LinkedList
数据结构 动态数组 双向链表
随机访问性能 快(O(1)) 慢(O(n),需遍历查找)
插入/删除头部 慢(O(n),需移动元素) 快(O(1),只需修改头节点指针)
插入/删除尾部 快(O(1),如果无需扩容) 快(O(1),只需修改尾节点指针)
插入/删除中间 慢(O(n),需移动元素) 较快(O(n),但不涉及元素移动,仅修改指针)
内存占用 因为是连续存储,所以内存使用较为紧凑 需要额外的空间存储指向前后的引用,内存开销较大
线程安全性 不是线程安全 不是线程安全
实现接口 List, RandomAccess List, Deque
适用场景 高效的随机访问,较少的插入和删除操作 高效的插入和删除操作,尤其是列表两端
  • Vector旧类,不建议使用 ):类似于 ArrayList ,但它是一个同步(线程安全)的集合,它的所有的方法都被隐式地同步了(所有方法 synchronized ),这使得 Vector 可以在多线程环境中安全使用。然而,由于同步带来的额外开销,使用这个类可能会导致性能下降。通常推荐在单线程环境下使用 ArrayList ;在多线程并发场景下,使用Collections.synchronizedList(new ArrayList<>());来创建一个线程安全的列表,或者直接使用并发集合如 CopyOnWriteArrayList
  • Stack旧类,不建议使用 ):扩展了 Vector ,并实现了后进先出(LIFO, Last In First Out)的数据结构,也是一个同步(线程安全)的集合。它主要用于栈操作。与 Vector 有同样的性能问题。官方文档推荐使用 Deque 接口(双端队列)的实现类,如 ArrayDequeLinkedList 代替。

Set接口实现类介绍

Set 集合不允许包含重复的元素(判断两个元素是否相同:根据元素的 equals() 方法判断);Set 不能像 List 一样可以通过整数索引来访问元素; Set 不保证元素的顺序,除非使用了特定类型的 Set 实现。

Set接口常用方法:

Set 接口本身提供的方法不多,但因为它继承自 Collection ,所以它可以使用 Collection接口中定义的所有方法,例如:

  • add(E e):如果Set中尚未包含指定的元素,则将其添加到此集合中。
  • remove(Object o):从此集合中移除指定元素(如果存在)。
  • contains(Object o):如果此集合包含指定的元素,则返回true
  • size():返回此集合中的元素数量。
  • clear():移除此集合中的所有元素。
  • iterator():返回在此集合中的元素上进行迭代的迭代器。

Set接口的常见实现类:

  • HashSet
    • 最常用的 Set 实现之一。
    • 不保证元素的顺序,且允许 null 值。
    • 通过哈希表 实现,提供了 O(1) 时间性能的基本操作(如添加、删除和查询)。
  • LinkedHashSet
    • 继承自 HashSet ,但是通过维护一个双向链表来记住元素插入的顺序。
    • 提供了与 HashSet 相同的操作效率,并额外支持按插入顺序遍历集合。
  • TreeSet
    • 基于红黑树的数据结构,能够对元素进行排序。
    • 元素必须实现 Comparable 接口,或者在创建 TreeSet 时提供一个 Comparator ,以决定元素的排序方式。
    • 支持自然排序或定制排序。
  • EnumSet
    • 特别为枚举类型设计的高效实现:通过一个位向量( bit vector )来高效地存储和操作枚举类型的元素。
    • 必须由同一个枚举类型的所有元素组成。
    • 提供了非常快速的操作性能。

Queue接口实现类介绍

Queue 是专门用于表示队列的数据结构,队列通常遵循**先进先出( FIFO, First-In-First-Out )**原则,即最早加入队列的元素将首先被移除。不过,也有例外情况,比如优先级队列,它根据元素的优先级来决定服务顺序。

此外,Java还提供了 Deque (双端队列)接口,允许从两端进行插入和删除操作,适用于栈、队列以及其他需要双向访问的数据结构。

Queue接口常用方法:

  • add(E e) / offer(E e):向队列尾部添加一个元素。 add() 方法在插入失败时抛出异常,而 offer() 返回一个布尔值。
  • remove() / poll():从队列头部移除并返回一个元素。remove() 在队列为空时抛出异常,而poll()则返回null
  • element() / peek():获取但不移除队列头部的元素。 element() 方法在队列为空时抛出异常,而 peek() 返回 null

Queue接口的常见实现类:

  • LinkedList:实现了 Queue 接口,并且可以作为双端队列使用。它允许在两端进行高效的插入和删除操作。
  • PriorityQueue:基于 的数据结构,按照元素的自然顺序或通过提供的 Comparator 来进行排序。因此,它并不严格遵守FIFO原则,而是根据优先级来确定哪个元素最先出队。

Map接口

Map 接口是集合框架的一部分,但它并不继承自 Collection 接口。 Map 用于存储键值对( key-value pairs ),其中每个键都是唯一的,而值可以重复。通过键可以在 Map 中高效地查找、更新或删除对应的值。

Map接口和类关系:

Map接口常用方法:

  • 基本操作:
    • put(K key, V value):将指定的键值对插入到 Map 中。
    • get(Object key):根据键返回对应的值。
    • remove(Object key):根据键移除键值对。
    • clear():移除 Map 中的所有映射关系。
    • isEmpty():检查 Map 是否为空。
    • size():返回 Map 中键值对的数量。
  • 查询操作:
    • containsKey(Object key):判断 Map 是否包含指定的键。
    • containsValue(Object value):判断 Map 是否包含指定的值。
    • keySet():返回 Map 中所有键的集合视图。
    • values():返回 Map 中所有值的集合视图。
    • entrySet():返回 Map 中所有键值对的集合视图。

Map接口的常见实现类:

  • HashMap
    • 最常用的实现之一,基于哈希表实现。
    • 允许 null 键和 null 值。
    • 不保证顺序。
  • LinkedHashMap
    • 继承自 HashMap ,但通过维护双向链表来记住元素的插入顺序。
    • 可以基于访问顺序进行排序(适合实现LRU缓存)。
  • TreeMap
    • 基于红黑树 数据结构,按键的自然顺序或自定义的 Comparator 进行排序。
    • 不允许 null 键。
  • Hashtable(旧类,不推荐使用)
    • 类似于 HashMap ,但是是同步的(线程安全),性能较差。
    • 不允许 null 键和 null 值。
    • 替代类:ConcurrentHashMap,专门设计用于高并发环境的线程安全HashMap实现,通过分段锁(在 Java 8 前)或数组 + 链表/红黑树(在 Java 8 及以后使用 synchronized 和 CAS )实现高效的并发访问,允许多个线程同时读写而不会导致数据竞争,从而提供高并发场景下的线程安全和性能优化。
  • EnumMap
    • 专门用于键为枚举类型的情况(要求所有键必须来自同一个枚举类型),针对枚举类型的键进行了特别优化,提供了更高效的存储和访问速度。
相关推荐
小王不爱笑13231 分钟前
Java项目基本流程(三)
java·开发语言
David爱编程44 分钟前
Java 三目运算符完全指南:写法、坑点与最佳实践
java·后端
遇见尚硅谷1 小时前
C语言:单链表学习
java·c语言·学习
学习编程的小羊1 小时前
Spring Boot 全局异常处理与日志监控实战
java·spring boot·后端
YA3332 小时前
java基础(六)jvm
java·开发语言
尘民10243 小时前
面试官笑了:线程start() 为什么不能再来一次?
java
会是上一次4 小时前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
Java中文社群5 小时前
抱歉!Java面试标准答案最不重要
java·后端·面试
Java小Y6 小时前
redis(2)-java客户端使用(IDEA基于springboot)
java·redis·intellij-idea