第2课_JDK集合框架源码解析
热度🔥:59 免费课程
授课语音
JDK集合框架及底层数据结构的原理解析
JDK集合框架是Java语言中用于存储和操作数据的核心工具之一。它为开发者提供了多种类型的数据容器,包括列表、集合、映射等。理解JDK集合框架及其底层数据结构的原理,有助于我们选择最合适的数据结构并优化程序的性能。本课程将详细解析JDK集合框架的组成、工作原理以及底层数据结构的实现。
1. JDK集合框架概述
JDK集合框架的核心思想是通过一组接口与类的组合,提供了一些常用的数据结构和算法。集合框架的主要接口包括:
- Collection:是所有集合类的根接口,继承自
Iterable
接口。 - List:继承自
Collection
,有序且可重复。 - Set:继承自
Collection
,无序且不允许重复元素。 - Map:用于存储键值对,键唯一,值可以重复。
1.1 集合框架的主要类与接口
- List接口的实现类有
ArrayList
、LinkedList
、Vector
等。 - Set接口的实现类有
HashSet
、LinkedHashSet
、TreeSet
等。 - Map接口的实现类有
HashMap
、TreeMap
、LinkedHashMap
等。
2. 集合框架的底层数据结构
集合框架的底层实现基于多种常见的数据结构,如数组、链表、树、哈希表等。下面我们逐一解析常见数据结构的原理和应用。
2.1 ArrayList:基于动态数组实现
ArrayList
是一个基于动态数组的集合,底层使用数组来存储元素。它的优点是支持快速随机访问,缺点是插入和删除操作较慢(特别是在中间位置插入或删除时)。
2.1.1 ArrayList源码解析
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable {
private transient Object[] elementData; // 用于存储元素的数组
private int size; // ArrayList中元素的个数
// 添加元素到ArrayList
public boolean add(E e) {
ensureCapacity(size + 1); // 保证有足够的容量
elementData[size++] = e; // 添加元素
return true;
}
// 确保容量
private void ensureCapacity(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
// 扩展数组的大小
int newCapacity = oldCapacity + (oldCapacity >> 1);
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
}
ArrayList
底层通过elementData
数组来存储元素。当添加新元素时,如果容量不足,就会扩展数组。扩展时,容量是以当前容量的1.5倍增长。
2.2 LinkedList:基于双向链表实现
LinkedList
是一个基于双向链表的数据结构,具有更快的插入和删除操作。特别是在链表的头部或尾部进行插入和删除时,效率非常高。它的缺点是随机访问效率较低,因为需要从链表的头部或尾部开始遍历。
2.2.1 LinkedList源码解析
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable {
private transient Node<E> first; // 链表的头结点
private transient Node<E> last; // 链表的尾结点
private transient int size = 0; // 链表的元素个数
// 添加元素到链表
public boolean add(E e) {
linkLast(e);
return true;
}
// 链接到链表的末尾
private void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null) {
first = newNode;
} else {
l.next = newNode;
}
size++;
}
}
LinkedList
底层通过Node
对象存储元素,每个Node
包含了当前元素、前一个节点和后一个节点的引用。当添加元素时,新的元素会被插入到链表的尾部。
2.3 HashSet:基于哈希表实现
HashSet
是一个无序的集合,它的底层实现是基于HashMap
。每个元素作为HashMap
的键存储,值是一个固定的常量。由于HashMap
是基于哈希表实现的,因此HashSet
具有很高的查询效率。
2.3.1 HashSet源码解析
public class HashSet<E> extends AbstractSet<E>
implements Set<E>, Cloneable, Serializable {
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}
}
HashSet
通过HashMap
的put
方法将元素作为键存储,其中PRESENT
是一个常量对象,用于填充值部分。这样就能保证集合中的元素唯一。
2.4 HashMap:基于哈希表实现
HashMap
是一个基于哈希表的键值对集合。它的底层是由数组和链表(或红黑树)组成的。当元素过多时,哈希冲突可能导致链表转换为红黑树,从而提升性能。
2.4.1 HashMap源码解析
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
private transient Node<K,V>[] table; // 哈希表数组
private transient int size; // 元素数量
public V put(K key, V value) {
int hash = hash(key);
int index = indexFor(hash, table.length); // 计算哈希值并确定存储位置
Node<K,V> node = table[index];
// 如果位置为空,则插入新的元素
if (node == null) {
table[index] = new Node<>(hash, key, value, null);
size++;
return null;
}
// 处理哈希冲突,遍历链表
for (Node<K,V> e = node; e != null; e = e.next) {
if (e.hash == hash && (e.key == key || (key != null && key.equals(e.key)))) {
V oldValue = e.value;
e.value = value; // 更新值
return oldValue;
}
}
// 如果没有找到相同的键,则插入新的元素
table[index] = new Node<>(hash, key, value, node);
size++;
return null;
}
}
HashMap
通过计算键的哈希值来确定元素的位置。如果发生哈希冲突,会采用链表法解决。如果链表的长度超过一定阈值,链表会转换为红黑树以提高查找效率。
3. 总结
JDK集合框架提供了丰富的接口和类,帮助我们高效地处理数据。在选择合适的集合类时,我们应根据实际需求来选择底层数据结构。例如:
- 当需要快速的随机访问时,选择
ArrayList
。 - 当频繁插入和删除时,选择
LinkedList
。 - 当需要保证元素唯一时,选择
HashSet
。 - 当需要存储键值对时,选择
HashMap
。
掌握集合框架及底层数据结构的原理,不仅能帮助我们更好地使用这些集合类,还能让我们在需要时自定义更高效的数据结构。