JAVA容器

在 Java 里,容器类的底层实现机制和设计思想十分复杂,下面将从多个维度深入剖析:

一、Collection 接口体系

1. List 接口
  • ArrayList
    • 动态扩容机制 :当数组容量不足时,会重新创建一个容量为原数组 1.5 倍的新数组,并把原数组的内容复制过去。比如,初始容量为 10,添加第 11 个元素时,就会触发扩容操作,新容量变为 15
    • Fail-Fast 机制 :在使用迭代器遍历 ArrayList 时,如果发现集合的结构被修改(像调用 addremove 方法),就会立刻抛出 ConcurrentModificationException,这一机制通过 modCount 计数器来实现。
  • LinkedList
    • 双向链表结构 :其每个节点(Node)都包含三个部分,即前驱节点引用、数据和后继节点引用。这使得在链表头部或尾部进行插入操作的时间复杂度为 O(1)
    • 多接口实现 :LinkedList 同时实现了 ListDeque 接口,所以它既可以当作列表使用,也能作为双端队列使用。
  • Vector & Stack
    • 线程安全的实现方式 :Vector 的所有公共方法都被 synchronized 修饰,不过这种方式会带来较大的性能开销。
    • Stack 的设计缺陷:Stack 继承自 Vector,这违背了组合复用原则,而且它暴露了 Vector 的所有方法,存在安全隐患。
2. Set 接口
  • HashSet
    • 底层依赖 HashMap :HashSet 内部使用一个 HashMap 来存储元素,元素被存放在 HashMap 的键中,而值则统一为一个静态常量 PRESENT
    • 哈希冲突的处理 :采用链表法(拉链法)来解决哈希冲突。当链表长度超过 8 且数组长度大于 64 时,链表会转化为红黑树。
  • TreeSet
    • 红黑树结构 :TreeSet 基于 TreeMap 实现,红黑树是一种自平衡的二叉搜索树,它能保证插入、删除和查询操作的时间复杂度都是 O(log n)
    • 元素比较 :TreeSet 要求元素必须实现 Comparable 接口,或者在创建 TreeSet 时传入一个 Comparator
3. Queue 接口
  • PriorityQueue
    • 堆结构:PriorityQueue 基于数组实现的二叉堆(通常是最小堆),父节点的值总是小于或等于子节点的值。
    • 排序规则 :元素需要实现 Comparable 接口,或者在创建 PriorityQueue 时传入 Comparator
  • ArrayDeque
    • 循环数组:ArrayDeque 使用循环数组来实现双端队列,通过头尾指针来标记队列的边界。
    • 无容量限制 :ArrayDeque 的容量会根据需要自动扩容,扩容时新容量是原容量的 2 倍。

二、Map 接口体系

1. HashMap
  • 哈希表结构 :在 JDK 8 及以后的版本中,HashMap 采用 数组 + 链表 + 红黑树 的结构。当链表长度超过 8 且数组长度大于 64 时,链表会转换为红黑树,以提高查询效率。
  • 哈希函数hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16),通过这种方式让高位数据也参与哈希运算,减少哈希冲突。
  • 扩容机制 :当元素数量超过 容量 × 负载因子(默认 0.75) 时,HashMap 会进行扩容,新容量是原容量的 2 倍,并且需要重新计算哈希值和元素位置。
2. LinkedHashMap
  • 双向链表维护顺序:LinkedHashMap 继承自 HashMap,它在 HashMap 的基础上增加了一个双向链表,用于记录元素的插入顺序或访问顺序。
  • 访问顺序(access-order) :当 accessOrdertrue 时,每次访问元素(如调用 get 方法),该元素会被移动到链表尾部,可用于实现 LRU 缓存。
3. TreeMap
  • 红黑树的应用:TreeMap 基于红黑树实现,能够根据键的自然顺序或指定的比较器对键值对进行排序。
  • 范围查询 :TreeMap 提供了 subMap()headMap()tailMap() 等方法,可用于高效地进行范围查询。
4. ConcurrentHashMap
  • 分段锁(JDK 7 及以前):在 JDK 7 及以前的版本中,ConcurrentHashMap 使用分段锁(Segment),每个 Segment 相当于一个小的 HashMap,不同的 Segment 可以并发访问。
  • CAS + synchronized(JDK 8 及以后) :JDK 8 及以后的版本摒弃了分段锁,采用 CAS(Compare-And-Swap)和 synchronized 来保证并发操作的安全性。对数组的每个槽位(Node)进行加锁,锁的粒度更小,并发性能更高。

三、设计模式与最佳实践

1. 迭代器模式
  • Iterator 接口 :Collection 接口继承了 Iterable 接口,所有实现 Collection 接口的类都需要提供 iterator() 方法,该方法返回一个 Iterator 对象,用于遍历集合中的元素。
  • ListIterator :List 接口还提供了 listIterator() 方法,返回的 ListIterator 支持双向遍历和元素修改操作。
2. 适配器模式
  • Arrays.asList():该方法可以将数组转换为 List,它返回的是一个固定大小的列表,不支持添加或删除元素操作。
  • Collections 工具类Collections.synchronizedList()Collections.unmodifiableSet() 等方法可以将非线程安全的集合转换为线程安全的集合,或者将可变集合转换为不可变集合。
3. 性能优化建议
  • 初始容量设置:在创建 ArrayList、HashMap 等容器时,合理设置初始容量,避免频繁扩容带来的性能开销。
  • 选择合适的容器
    • 如果需要频繁随机访问元素,可选择 ArrayList。
    • 如果需要频繁插入或删除元素,可选择 LinkedList。
    • 如果需要保证线程安全,在单线程环境下可使用 Collections.synchronizedXXX(),在多线程环境下推荐使用 ConcurrentHashMap。

四、并发容器详解

1. BlockingQueue
  • 阻塞操作 :BlockingQueue 提供了 put()take() 方法,当队列满时,put() 方法会阻塞;当队列空时,take() 方法会阻塞。
  • 应用场景 :常用于生产者 - 消费者模式,典型实现有 ArrayBlockingQueueLinkedBlockingQueue 等。
2. CopyOnWriteArrayList
  • 写时复制机制 :在进行写操作(如 addremove)时,CopyOnWriteArrayList 会先复制一份新的数组,在新数组上进行修改,修改完成后再将原数组的引用指向新数组。
  • 适用场景:适用于读多写少的场景,因为读操作不需要加锁,性能较高。

五、容器类的常见陷阱

1. 泛型擦除
  • 原理 :Java 的泛型在编译时会进行类型擦除,例如 List<Integer>List<String> 在运行时实际上是相同的类型。
  • 影响:无法在运行时获取泛型的具体类型,可能会导致一些类型转换异常。
2. 快速失败机制
  • Fail-Fast 与 Fail-Safe
    • Fail-Fast(如 ArrayList):在遍历过程中,如果发现集合结构被修改,会立即抛出异常。
    • Fail-Safe(如 CopyOnWriteArrayList):在遍历过程中允许集合结构被修改,因为它遍历的是原集合的副本。
3. 内存泄漏
  • 强引用问题:如果容器持有对象的强引用,即使对象在其他地方已经不再使用,垃圾回收器也无法回收该对象,从而导致内存泄漏。
  • 解决方案 :可以使用 WeakHashMap 等弱引用容器,当对象的其他强引用被释放后,容器中的弱引用不会阻止对象被垃圾回收。

六、Java 9+ 的增强

1. 不可变集合工厂方法
  • 示例List.of()Set.of()Map.of() 等方法可以创建不可变集合,这些集合不允许添加、删除或修改元素。
  • 优势:代码更简洁,避免了意外修改集合的风险。
2. Stream API
  • 流式操作 :容器类可以通过 stream()parallelStream() 方法获取流,然后进行过滤、映射、聚合等操作,支持函数式编程。

总结

Java 容器类的设计充分体现了面向对象、数据结构和并发编程的思想。深入理解容器类的底层实现,有助于我们在实际开发中选择合适的容器,优化代码性能,避免常见的陷阱。

相关推荐
季鸢5 分钟前
Java设计模式之观察者模式详解
java·观察者模式·设计模式
Fanxt_Ja17 分钟前
【JVM】三色标记法原理
java·开发语言·jvm·算法
Mr Aokey1 小时前
Spring MVC参数绑定终极手册:单&多参/对象/集合/JSON/文件上传精讲
java·后端·spring
小马爱记录2 小时前
sentinel规则持久化
java·spring cloud·sentinel
长勺2 小时前
Spring中@Primary注解的作用与使用
java·后端·spring
紫乾20142 小时前
idea json生成实体类
java·json·intellij-idea
wh_xia_jun2 小时前
在 Spring Boot 中使用 JSP
java·前端·spring boot
网安INF2 小时前
CVE-2020-17518源码分析与漏洞复现(Flink 路径遍历)
java·web安全·网络安全·flink·漏洞
Y第五个季节2 小时前
docker-部署Nginx以及Tomcat
java·开发语言
IT-ZXT8883 小时前
Tomcat 线程模型详解&性能调优
java·tomcat