Jtoss Jtoss
首页
  • 数据结构与算法

    • 数据结构与算法 - 概述
    • 数据结构与算法 - 复杂度分析
    • 数据结构 - 线性表
    • 算法 - 常见排序算法
  • 代码规范

    • 代码简洁之道
    • 阿里巴巴开发手册
    • 谷歌Java编程风格指南
  • 设计模式

    • 编写高质量代码概述
    • 面向对象
    • 设计原则
    • 设计模式-创建型
    • 设计模式-结构型
    • 设计模式-行为型(上)
    • 设计模式-行为型(下)
    • 浅析框架源码中的设计模式
    • 业务框架实战案例
  • MySQL 基础

    • MySQL - 数据库设计规范
    • MySQL - 必知必会
  • MySQL 进阶

    • MySQL - 基础架构
    • MySQL - InnoDB存储引擎
    • MySQL - InnoDB缓冲池
    • MySQL - 事务与锁
    • MySQL - 索引
    • MySQL - 查询执行计划
    • MySQL - 性能优化
  • Redis 系列

    • Redis入门 - 基础相关
    • Redis进阶 - 数据结构
    • Redis进阶 - 持久化RDB和AOF
    • Redis进阶 - 事件机制
    • Redis进阶 - 事务
    • Redis进阶 - 高可用高可扩展
    • Redis进阶 - 缓存问题
    • Redis进阶 - 性能调优
  • Java 基础

    • Java 基础 - 知识点
    • Java 基础 - 面向对象
    • Java 基础 - Q/A
  • Java 进阶 - 集合框架

    • Java 集合框架详解
  • Java 进阶 - 多线程与并发

    • Java 并发 - 理论基础
    • Java 并发 - 线程基础
    • Java 并发 - 各种锁
    • Java 并发 - 关键字 volatile
    • Java 并发 - 关键字 synchronized
    • JUC - CAS与原子操作
    • JUC - 锁核心类AQS
    • JUC - 锁接口和类简介
    • JUC - 并发容器简介
    • JUC - 通信工具类
    • JUC - Fork-Join框架
    • JUC - 线程池
  • Java 进阶 - JVM

    • JVM - 概述
    • JVM - 类加载机制
    • JVM - 内存结构
    • JVM - 垃圾回收机制
    • JVM - 性能调优
  • Maven系列

    • Maven基础知识
    • Maven项目构建
    • Maven多模块配置
  • Spring 框架

    • Spring 框架 - 框架介绍
    • Spring 框架 - IOC详解
    • Spring 框架 - AOP详解
    • Spring 框架 - SpringMVC详解
  • Spring Boot 系列

    • Spring Boot - 开发入门
    • Spring Boot - 接口相关
  • Spring Cloud 系列
  • Mybatis 系列

    • Mybatis - 总体框架设计
    • Mybatis - 初始化基本过程
    • Mybatis - sqlSession执行过程
    • Mybatis - 插件机制
    • Mybatis - 事务管理机制
    • Mybatis - 缓存机制
  • 业务常见问题

    • Java 业务开发常见错误(一)
    • Java 业务开发常见错误(二)
    • Java 业务开发常见错误(三)
    • Java 业务开发常见错误(四)
    • Java 业务开发常见错误(五)
    • Java 业务开发常见错误(六)
  • IDEA系列

    • IDEA 2021开发环境配置
    • IDEA 快捷键
  • Git系列

    • git status中文乱码
  • 其他

    • Typora+Picgo 自动上传图片
    • hsdis 和 jitwatch
  • 实用技巧
  • 收藏
  • 摄影
  • 学习
  • 标签
  • 归档

Jason Huang

后端程序猿
首页
  • 数据结构与算法

    • 数据结构与算法 - 概述
    • 数据结构与算法 - 复杂度分析
    • 数据结构 - 线性表
    • 算法 - 常见排序算法
  • 代码规范

    • 代码简洁之道
    • 阿里巴巴开发手册
    • 谷歌Java编程风格指南
  • 设计模式

    • 编写高质量代码概述
    • 面向对象
    • 设计原则
    • 设计模式-创建型
    • 设计模式-结构型
    • 设计模式-行为型(上)
    • 设计模式-行为型(下)
    • 浅析框架源码中的设计模式
    • 业务框架实战案例
  • MySQL 基础

    • MySQL - 数据库设计规范
    • MySQL - 必知必会
  • MySQL 进阶

    • MySQL - 基础架构
    • MySQL - InnoDB存储引擎
    • MySQL - InnoDB缓冲池
    • MySQL - 事务与锁
    • MySQL - 索引
    • MySQL - 查询执行计划
    • MySQL - 性能优化
  • Redis 系列

    • Redis入门 - 基础相关
    • Redis进阶 - 数据结构
    • Redis进阶 - 持久化RDB和AOF
    • Redis进阶 - 事件机制
    • Redis进阶 - 事务
    • Redis进阶 - 高可用高可扩展
    • Redis进阶 - 缓存问题
    • Redis进阶 - 性能调优
  • Java 基础

    • Java 基础 - 知识点
    • Java 基础 - 面向对象
    • Java 基础 - Q/A
  • Java 进阶 - 集合框架

    • Java 集合框架详解
  • Java 进阶 - 多线程与并发

    • Java 并发 - 理论基础
    • Java 并发 - 线程基础
    • Java 并发 - 各种锁
    • Java 并发 - 关键字 volatile
    • Java 并发 - 关键字 synchronized
    • JUC - CAS与原子操作
    • JUC - 锁核心类AQS
    • JUC - 锁接口和类简介
    • JUC - 并发容器简介
    • JUC - 通信工具类
    • JUC - Fork-Join框架
    • JUC - 线程池
  • Java 进阶 - JVM

    • JVM - 概述
    • JVM - 类加载机制
    • JVM - 内存结构
    • JVM - 垃圾回收机制
    • JVM - 性能调优
  • Maven系列

    • Maven基础知识
    • Maven项目构建
    • Maven多模块配置
  • Spring 框架

    • Spring 框架 - 框架介绍
    • Spring 框架 - IOC详解
    • Spring 框架 - AOP详解
    • Spring 框架 - SpringMVC详解
  • Spring Boot 系列

    • Spring Boot - 开发入门
    • Spring Boot - 接口相关
  • Spring Cloud 系列
  • Mybatis 系列

    • Mybatis - 总体框架设计
    • Mybatis - 初始化基本过程
    • Mybatis - sqlSession执行过程
    • Mybatis - 插件机制
    • Mybatis - 事务管理机制
    • Mybatis - 缓存机制
  • 业务常见问题

    • Java 业务开发常见错误(一)
    • Java 业务开发常见错误(二)
    • Java 业务开发常见错误(三)
    • Java 业务开发常见错误(四)
    • Java 业务开发常见错误(五)
    • Java 业务开发常见错误(六)
  • IDEA系列

    • IDEA 2021开发环境配置
    • IDEA 快捷键
  • Git系列

    • git status中文乱码
  • 其他

    • Typora+Picgo 自动上传图片
    • hsdis 和 jitwatch
  • 实用技巧
  • 收藏
  • 摄影
  • 学习
  • 标签
  • 归档
  • Java 基础

  • Java 进阶 - 集合框架

    • Collections 框架概览
      • 框架介绍
      • 接口和实现
      • Collection 三大集合类
        • List
        • !image1
        • Set
        • Queue/Deque
      • Map
        • TreeMap
        • HashMap
        • HashTable
        • LinkedHashMap
      • 考察点
        • 数据结构
        • 算法
      • 参考
    • Collection - ArrayList & Vector 源码详解
    • Collection - CopyOnWriteArrayList 源码解析
    • Collection - LinkedList 源码详解
    • Collection - ArrayDeque 源码解析
    • Collection - PriorityQueue 源码解析
    • Map - HashMap & HashSet 源码解析
    • Map - LinkedHashMap & LinkedHashSet 源码解析
    • Map - TreeSet & TreeMap 源码解析
    • Map - WeekHashMap 源码解析
  • Java 进阶 - 多线程与并发

  • Java 进阶 - JVM

  • Java 进阶 - 版本特性

  • Java
  • Java 进阶 - 集合框架
Jason
目录

Collections 框架概览

# Collections 框架概览

# 框架介绍

容器,就是可以容纳其他Java对象的对象。*Java Collections Framework(JCF)*为Java开发者提供了通用的容器,其始于JDK 1.2,优点是:

  • 降低编程难度
  • 提高程序性能
  • 提高API间的互操作性
  • 降低学习难度
  • 降低设计和实现相关API的难度
  • 增加程序的重用性

Java 容器里只能放对象,对于基本类型(int, long, float, double 等),需要将其包装成对象类型后(Integer, Long, Float, Double 等)才能放到容器里。很多时候拆包装和解包装能够自动完成。这虽然会导致额外的性能和空间开销,但简化了设计和编程。

Java 容器能够容纳任何类型的对象,这一点表面上是通过泛型机制完成,Java 泛型不是什么神奇的东西,只是编译器为我们提供的一个“语法糖”,泛型本身并不需要 Java 虚拟机的支持,只需要在编译阶段做一下简单的字符串替换即可。

由于Java 里对象都在堆上,且对象只能通过引用访问,容器里存放的其实是对象的引用而不是对象本身。

# 接口和实现

为了规范容器的行为和统一设计,JCF 定义了 14 种容器接口(collection interfaces),它们的关系如下图所示:

collections1

Map接口没有继承自 Collection 接口,因为 Map 表示的是关联式容器而不是集合。但 Java 为我们提供了从 Map 转换到 Collection 的方法,可以方便的将 Map 切换到集合。 上图中提供了Queue 接口,却没有 Stack,这是因为 Stack 的功能已被JDK 1.6 引入的Deque取代。

Java collection cheat sheet

JCF 容器的数据结构和算法时间复杂度列表:

Java collection datastructure

# Collection 三大集合类

Collection 是所有集合的基本接口,Collection 接口继承 Iterable 接口,定义了对集合的通用操作方法,如 add()、remove()、contains()、size()、iterator()等。iterator() 方法返回一个实现 Iterator 接口的对象,可以使用迭代器访问集合中的所有元素。

Java 的迭代器可以认为是位于两个元素之间,当调用 next 时,迭代器就越过下个元素,并返回刚刚越过的那个元素的引用。

对有实现 Iterable 接口的对象采用 foreach 语法糖,Java 编译器会将 foreach 语句转换为迭代器方式,如;

Collection<String> c = ...;
for (String element : c) {
    // do something with element
}

// 编译器会将上面代码编译为迭代器
Iterator<String> ite = c.iterator();
while (ite.hasNext()) {
    String element = ite.next();
    // do something with element;
}
1
2
3
4
5
6
7
8
9
10
11

由 Collection 接口扩展出三大集合类:

  • List:有序集合,提供方便的访问、插入、删除等操作
  • Set:无序集合,不允许重复元素
  • Queue / Deque:标准队列结构,除了集合的基本功能,还支持先进先出,后进先出等特性功能

每种集合的通用逻辑都被抽象到相应的抽象类中,如 AbstractList、AbstractSet、AbstractQueue。

Collections 工具类:提供了创建线程安全的集合静态方法,原理是在集合添加、删除、截取等修改操作前添加 synchronized 关键字。

# List

有序集合

Vector、ArrayList、LinkedList ,它们都实现了 Java 集合框架的 List 接口,都是有序集合。功能相似,都提供按照位置进行定位、添加或删除等操作,都提供迭代器遍历所有元素。区别如下:

  • Vector:动态数组,线程安全(方法都是同步的 synchronized ),自动扩容,每次扩容1倍;内部元素以数组形式顺序存储(随机访问效率高 item.get(n)),插入或删除效率低
  • ArrayList:动态数组,非线程安全,自动扩容,每次扩容50%,性能比 Vector 好;内部元素以数组形式顺序存储(随机访问效率高 item.get(n)),插入或删除效率低
  • LinkedList:双向链表,非线程安全,无需扩容;节点插入或删除效率高,随机访问效率低;

Vector 和 ArrayList 的缺陷:

Vector 与 ArrayList 内部元素以数组形式顺序存储,适合随机访问的场合(索引序列,随机访问效率高);指定位置插入或删除效率很低(除在尾部插入或删除),因需要前移或后移指定位置开始的所有元素。

# image1

LinkedList 解决了这个问题

链表结构解决了数组结构删除和插入元素效率低的问题。链表(Java中链表都是双向链表)的每个节点存放对象数据、下一个节点的引用和上一个节点的引用。要想删除元素只需修改被删除元素附近的链接即可。unlink(Node<E> x)

image2

但 LinkedList 的缺点也很明细,在要访问第 n 个元素时,必须从头开始,越过 n-1 个元素才能得到。所以当程序需要采用整数索引访问元素时,通常不选用链表。

小结:

  • 需要采用整数索引访问列表元素时,使用 ArrayList 或 Vector(线程安全);
  • 列表只有少量元素,插入和删除操作代价不高,可以使用 ArrayList 或 Vector(线程安全)
  • 列表元素比较多,且需要插入和删除操作,使用 LinkedList。

# Set

不允许存在重复元素,快速查找指定元素

场景:查看某个指定元素,但不知其位置,则需要遍历整个集合直到找到为止。如果集合的元素很多,将消耗很多时间

**解决方式:**有几种快速查找元素的数据结构,如下:

  • 散列表(hash table):无序
  • 红黑树:有序

散列表:一种可以快速查找所需对象的数据结构。散列表为每一个对象计算一个整数,称为散列码(hash code)。具有不同数据域的对象将产生不同的散列码。

Java 中散列表使用链表数组实现。每个列表称为桶(bucket),要想查找表中的对象位置,要先计算出对象的散列码,然后与桶的总数相除取余,所得到的结果就是保存这个桶的索引。

例如:某个对象的散列码是 76268,并且有 128 个桶,对象应该保存在第 108 号桶中(76268 / 128 余 108)。如果桶中没有其他元素则直接插入到桶中即可。

若知道会有多少个元素要插入散列表中,可以设置初始桶数为元素总数的 75% - 150%;标准类库默认的桶数为16;

如果散列表太满,就需要再散列。装填因子的值决定什么时候对散列表进行再散列,其值默认为 0.75。即表在超过 75% 的位置已经填入元素,这个表就用双倍桶数自动进行再散列。

HashMap_base

TreeSet、HashSet、LinkedHashSet ,它们都实现了 Java 集合框架的 Set 接口。区别如下:

  • TreeSet:树集,红黑树数据结构,支持自然顺序访问,但添加、删除、包含等操作相对低效 O(logN) 时间,有序集合
  • HashSet:散列集,散列表数据结构,理想情况下,如果哈希散列正常,可以提供常数时间 O(1) 的添加、删除、包含等操作。无序集合。
  • LinkedHashSet:散列表数据结构,内部构建了一个记录插入顺序的双向链表,因此提供了按照插入顺序遍历的能力,与此同时,也保证了常数时间的添加、删除、包含等操作,这些操作性能略低于 HashSet,因为需要维护链表的开销
  • 在遍历元素时,HashSet 性能受自身容量影响,所以初始化时,除非有必要,不然不要将其背后的 HashMap 容量设置过大。而对于 LinkedHashSet,由于其内部链表提供的方便,遍历性能只和元素多少有关系

# Queue/Deque

标准队列结构

Queue 队列:可以在尾部添加一个元素,在头部删除一个元素。

Deque 双端队列:可以在尾部和头部同时添加和删除元素

PriorityQueue、ArrayQueue 区别:

  • PriorityQueue:优先级队列,一种高效删除最小元素的集合,使用堆数据结构,按任意顺序插入,但删除时是最小元素。适用于任务调度。当调用peek()或者poll()方法获取队列元素时,获取的是队列最小元素,而不是先入队列的元素。有点反先入先出规则。
  • ArrayQueue:底层实现类似于 ArrayList,都是通过动态、可分配的 Object[] 数组来实现元素存储,当集合元素超过数组容量,会重新分配一个新的数组来存储集合元素。

# Map

# TreeMap

基于红黑树实现。树映射,用键的整体顺序进行排序,并将其组织成搜索树。速度慢

# HashMap

基于哈希表实现。散列映射,对键进行散列。速度快,无需按照排列顺序访问键,最好选择散列映射

# HashTable

和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。

# LinkedHashMap

使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。

# 考察点

Java 集合框架考察点 ,有很多方面需要掌握:

  • Java 集合框架的设计结构,至少要有一个整体印象。
  • Java 提供的主要容器(集合和 Map)类型,了解或掌握对应的数据结构、算法,思考具体技术选择。
  • 将问题扩展到性能、并发等领域。
  • 集合框架的演进与发展。

# 数据结构

数组、双向链表、队列、散列表、二叉树、红黑树、堆等等

# 算法

典型排序算法为例,至少需要熟知:

  • 内部排序,至少掌握基础算法如归并排序、交换排序(冒泡、快排)、选择排序、插入排序等。
  • 外部排序,掌握利用内存和外部存储处理超大数据集,至少要理解过程和思路。

考察算法不仅仅是如何简单实现,面试官往往会刨根问底,比如哪些是排序是不稳定的呢(快排、堆排),或者思考稳定意味着什么;对不同数据集,各种排序的最好或最差情况;从某个角度如何进一步优化(比如空间占用,假设业务场景需要最小辅助空间,这个角度堆排序就比归并优异)等

# 参考

https://github.com/CarpenterLee/JCFInternals

#Java#Collections
上次更新: 2024-08-19
扩展 - 反射和动态代理
Collection - ArrayList & Vector 源码详解

← 扩展 - 反射和动态代理 Collection - ArrayList & Vector 源码详解→

最近更新
01
开始
01-09
02
AI工具分享
01-09
03
AI 导读
01-07
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Jason Huang | 闽ICP备2025088096号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式