Java集合框架总结(面试八股)

Java集合框架可以分为两条大的支线:

  1. Collection,主要由List、Set、Queue组成:
    List代表有序,可重复的集合,典型代表就是封装了动态数组的ArrayList和封装了链表的LinkedList
    Set代表无序,不可重复的集合,典型代表就是HashSet和TreeSet;
    Queue代表队列典型代表就是双端队列ArrayDeque,以及优先级队列PriorityQueue;
  2. Map,代表键值对的集合,典型代表就是HashMap。

List

List的特点是存取有序,可以存放重复的元素,可以索引访问,允许null值。

|----------------|------|-----------------|------|--------|----------|---------------|-------------|---------------|
| 实现类 | 底层结构 | 线程安全 | 顺序 | 允许null | 查询性能 | 插入/删除性能 | 扩容机制 | 适用场景 |
| ArrayList | 动态数组 | 否 | 插入顺序 | 允许 | O(1)随机访问 | 尾部O(1),中间O(n) | 默认10,1.5倍扩容 | 随机访问频繁,数据量不大 |
| LinkedList | 双向链表 | 否 | 插入顺序 | 允许 | O(n)顺序访问 | O(1)头尾操作 | 无扩容,节点动态创建 | 频繁插入删除,队列/栈操作 |
| Vector | 动态数组 | 是(synchronized) | 插入顺序 | 允许 | O(1)随机访问 | 尾部O(1),中间O(n) | 默认10,可指定增量 | 遗留代码,需要线程安全 |

Set

Set的特点是存取无序、不可以存放重复的元素,无索引访问

|---------------------------|-----------------------|------|------------|--------|----------|------------------------|-------------|---|
| 实现类 | 底层结构 | 线程安全 | 顺序 | 允许null | 性能 | 唯一性判断 | 适用场景 | |
| HashSet | HashMap | 否 | 无序 | 允许 | O(1) | hashCode() + equals() | 快速去重,不关心顺序 |
| LinkedHashSet | LinkedHashMap | 否 | 插入顺序/访问顺序 | 允许 | O(1) | hashCode() + equals() | 需要保持插入顺序的去重 |
| TreeSet | TreeMap(红黑树) | 否 | 自然排序/自定义排序 | 不允许 | O(log n) | compareTo()/Comparator | 需要排序的去重集合 |
| ConcurrentSkipListSet | ConcurrentSkipListMap | 是 | 自然排序/自定义排序 | 不允许 | O(log n) | compareTo()/Comparator | 并发有序集合 |

Map

Map保存的是键值对,键要求保持唯一性,值可以重复。

|-------------------|------------------|--------|-----------|----------|----------|-----------|--------------|
| 实现类 | 底层结构 | 线程安全 | 顺序 | 允许null键值 | 性能 | 扩容机制 | 适用场景 |
| HashMap | 数组+链表/红黑树(JDK8+) | 否 | 无序 | 键值都允许 | O(1)平均 | 默认16,2倍扩容 | 通用键值对存储,性能优先 |
| LinkedHashMap | 数组+链表+双向链表 | 否 | 插入顺序/访问顺序 | 键值都允许 | O(1)平均 | 同HashMap | 需要保持插入/访问顺序 |
| TreeMap | 红黑树 | 否 | Key的自然排序 | 键不允许 | O(log n) | 无扩容 | 需要按键排序的场景 |
| Hashtable | 数组+链表 | 是(全表锁) | 无序 | 键值都不允许 | O(1)平均 | 默认11,2n+1 | 遗留代码,线程安全 |

总结(List,Set,Map)

|------------|--------------------------------|--------------------------------------------|-----------------------------------------|
| 特性 | List | Set | Map |
| 存储结构 | 有序序列 | 唯一元素集合 | 键值对映射 |
| 元素重复 | 允许 | 不允许 | Key不允许重复,Value允许重复 |
| 顺序保证 | 插入顺序 | HashSet无序,TreeSet排序,LinkedHashSet插入顺序 | HashMap无序,TreeMap按键排序,LinkedHashMap插入顺序 |
| null处理 | 允许null | HashSet允许null,TreeSet不允许 | HashMap允许null键值,Hashtable不允许 |
| 访问方式 | 索引访问 | 迭代器遍历 | 通过Key访问Value |
| 主要实现 | ArrayList, LinkedList | HashSet, TreeSet, LinkedHashSet | HashMap, TreeMap, LinkedHashMap |
| 线程安全 | Vector, CopyOnWriteArrayList | CopyOnWriteArraySet, ConcurrentSkipListSet | Hashtable, ConcurrentHashMap |
| 性能特点 | ArrayList随机访问快,LinkedList插入删除快 | HashSet查询快,TreeSet有序 | HashMap查询快,TreeMap有序 |
| 使用场景 | 有序、可重复的数据集合 | 去重、集合运算 | 键值对查找、缓存、配置 |

基础八股文:

1,简述Java的Set

Set即集合,该数据结构不允许元素重复且无序。Java对Set有三种实现方式。
HashSet通过HashMap实现,HashMap的key即HashSet存储的元素,Value系统自定义一个名为PRESENT的Object类型常量。判断元素是否相同时,先比较hashCode,相同后再利用equals比较,查询效率为O(1)。
LinkHashSet继承自HashSet,通过LinkedHashMap实现,使用双向链表维护元素插入顺序。
TreeSet通过TreeMap实现的,底层数据结构是红黑树,添加元素到集合时按照比较规则将其插入合适的位置,保证插入后的集合仍然有序。查询效率为O(logn)

2,简述Java中的HashMap

JDK8之前底层实现是数组+链表,JDK8改为数组+链表/红黑树。主要成员变量包括存储数据的table数组,元素数量site、加载因子loadFactor。HashMap中数据以键值对的形式存在,键对应的hash值用来计算数组下标,如果两个元素的key的hash值一样,就会发生哈希冲突,被放到同一个链表上。
table数组记录HashMap的数据,每个下标对应一条链表。所有哈希冲突的数据都会被存放到同一条链表,Node/Entry节点包含四个成员变量:key、value、next指针和hash值。在JDK8之后链表超过8会转化成红黑树。
若当前数据/总数据容量>负载因子,HashMap将执行扩容操作。默认初始化容量为16,扩容容量必须是2的幂次方、最大容量为1 << 30 默认加载因子为 0.75。

3,为何HashMap线程不安全

在JDK1.7中,HashMap采用头插法插入元素,因此并发情况下会导致环形链表,产生死循环。
虽然JDK1.8采用了尾插法解决了这个问题,但是并发下的put操作也会使前一个key被后一个key覆盖。
由于HashMap有扩容机制存在,也存在A线程进行扩容后,B线程执行get方法出现失误的情况。

4,简述Java的TreeMap

TreeMap是底层利用红黑树实现的Map结构,底层实现是一颗平衡的排序二叉树,由于红黑树的插入、删除、遍历时间复杂度都为O(logN),所以性能上低于哈希表。但是哈希表无法提供键值对的有序输出,红黑树可以按照键的值的大小有序输出。

ArrayList,Vector和LinkedList有什么共同点和区别?

  • ArrayList、Vector和LinkedList都是可伸缩的数组,即可以动态改变长度的数组。
  • ArrayList、Vector都是基于存储元素的Object[] array来实现的,他们会在内存中开辟一块连续的空间来存储,支持下标,索引访问。但在涉及插入元素时可能需要移动容器中的元素,插入效率较低。当存储元素超过容器的初始化容量大小,ArrayList与Vector均会进行扩容。
  • Vector是线程安全的,其大部分方法是直接或间接同步的。ArrayList不是线程安全的,其方法不具有同步性质。LinkedList也不是线程安全的。
  • LinkedList采用双向列表实现,对数据索引需要从头开始遍历,因此随机访问效率较低,但在插入元素的时候不需要对数据进行移动,插入效率较高。
HashMap和Hashtable有什么区别?
  • HashMap是Hashtable的轻量级实现,HashMap允许key和value为null,但最多允许一条记录的key为null,HashTable不允许。
  • HashTable中的方法是线程安全的,而HashMap不是。在多线程访问HashMap需要提供额外的同步机制。
  • Hashtable使用Enumeration进行遍历,HashMap使用Interator进行遍历。
相关推荐
ejjdhdjdjdjdjjsl2 小时前
C#文件流操作技巧
java·开发语言·spring
虾说羊2 小时前
HashMap详解
java
lkbhua莱克瓦242 小时前
反射3-反射获取构造方法
java·开发语言·反射
wanghowie2 小时前
02.04.01 Java Stream API 进阶指南:从底层实现到性能优化
java·开发语言·性能优化
专注于大数据技术栈2 小时前
java学习--Date
java·学习
superman超哥3 小时前
仓颉元编程进阶:编译期计算能力的原理与深度实践
开发语言·后端·仓颉编程语言·仓颉·仓颉语言·仓颉元编程·编译器计算能力
青莲8433 小时前
Java基础篇——第三部
java·前端
这周也會开心3 小时前
Map集合的比较
java·开发语言·jvm