本篇文章涵盖 ArrayList/LinkedList 源码细节、HashMap 完整原理(含红黑树、扩容、扰动函数)、Set 底层实现、迭代器 fail-fast 机制、并发容器简介、Comparable/Comparator 深入、Collections 工具类全解、Properties 详解 以及 实际开发建议。所有代码均可直接运行。
Iterable (根接口,提供 for-each 支持)
│
└── Collection
├── List
├── Set
└── Queue
Map (独立分支)
Collection:单值存储。Map:键值对存储。
transient Object[] elementData; // 存储元素的数组 private int size; // 实际元素个数
// 无参构造:初始为空数组,第一次 add 时扩容到 10
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 指定初始容量
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
oldCapacity + oldCapacity / 2)。Arrays.copyOf 复制原数组,开销较大。ArrayList(int initialCapacity) 减少扩容。| 操作 | 时间复杂度 | 说明 |
|---|---|---|
尾部添加 add(e) | 均摊 O(1) | 偶尔扩容 O(n) |
指定位置添加 add(i, e) | O(n) | 需要移动元素 |
尾部删除 remove(size-1) | O(1) | 直接置 null |
指定位置删除 remove(i) | O(n) | 移动元素 |
随机访问 get(i) | O(1) | 数组特性 |
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
transient int size = 0; transient Node<E> first; transient Node<E> last;
addFirst、addLast、removeFirst、removeLast)。get(index):O(n),需要通过循环或二分折半查找。实现了 Deque 接口,可当作栈、队列使用。
Deque<String> stack = new LinkedList<>();
stack.push("A"); // 压栈
String top = stack.pop(); // 弹栈
Deque<String> queue = new LinkedList<>();
queue.offer("A"); // 入队
String head = queue.poll(); // 出队
Vector 与 ArrayList 类似,但方法使用 synchronized 修饰,线程安全,但性能差。Stack 继承 Vector,被 Deque 取代。public class HashSet<E> extends AbstractSet<E> implements Set<E>, ... {
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}
}
HashSet 的元素作为 HashMap 的 Key,Value 是一个固定的占位对象 PRESENT。HashMap 的 Key 唯一性保证。HashSet 判断两个对象是否相等:先比较 hashCode(),若相等再比较 equals()。equals 而不重写 hashCode,则两个逻辑相等的对象可能被放入不同桶中,导致重复。HashSet,底层使用 LinkedHashMap,维护插入顺序的双向链表。TreeMap(红黑树),元素可排序。Comparable 或在构造时传入 Comparator。Set<String> treeSet = new TreeSet<>(); // 自然排序 Set<String> treeSet2 = new TreeSet<>(Comparator.reverseOrder()); // 倒序
| 常量 | 默认值 | 说明 |
|---|---|---|
DEFAULT_INITIAL_CAPACITY | 16 | 默认初始容量 |
MAXIMUM_CAPACITY | 1 << 30 | 最大容量 |
DEFAULT_LOAD_FACTOR | 0.75f | 默认加载因子 |
TREEIFY_THRESHOLD | 8 | 链表长度 ≥ 8 且数组长度 ≥ 64 转红黑树 |
UNTREEIFY_THRESHOLD | 6 | 树节点 ≤ 6 转回链表 |
MIN_TREEIFY_CAPACITY | 64 | 链表树化最小数组长度 |
hash 值:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)(扰动函数,使高位参与运算,减少碰撞)。(n - 1) & hash(等价于 hash % n,但位运算更快)。equals),是则覆盖。TREEIFY_THRESHOLD,是则转为红黑树。++size > threshold,是则扩容。size > capacity * loadFactor。oldCap << 1(翻倍)。原位置 + oldCap,只需判断原 hash 的第 oldCap 位是否为 1,效率很高。// 扩容后重新分配元素(简化示意)
if ((e.hash & oldCap) == 0) {
// 保持原位置
} else {
// 移动到 oldIndex + oldCap
}
HashMap,内部维护双向链表记录插入顺序或访问顺序(accessOrder)。// 构造 accessOrder=true 表示按访问顺序排序 LinkedHashMap<K,V> lru = new LinkedHashMap<K,V>(16, 0.75f, true);
subMap、headMap、tailMap 等范围视图。每个集合有一个内部类实现 Iterator 接口,维护以下字段:
cursor:下一个返回元素的位置。lastRet:最后一次返回的索引(用于 remove)。expectedModCount:迭代器期望的集合修改次数(初始为 modCount)。modCount 记录集合结构性修改次数(添加、删除、扩容等)。modCount == expectedModCount,若不相等则抛出 ConcurrentModificationException,防止并发修改。remove 方法(会同步更新 expectedModCount)。CopyOnWriteArrayList(写时复制,适合读多写少)。ConcurrentHashMap 等)。// 正确删除
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("B")) {
it.remove();
}
}
// Java 8 推荐的删除方式
list.removeIf(s -> s.equals("B"));
CopyOnWriteArrayList)在迭代时使用的是原集合的快照,不会抛异常,但无法感知迭代过程中的修改。
| 方法 | 描述 | 示例 |
|---|---|---|
sort(List) | 自然排序 | Collections.sort(list) |
sort(List, Comparator) | 定制排序 | Collections.sort(list, (a,b)->a-b) |
reverse(List) | 反转顺序 | Collections.reverse(list) |
shuffle(List) | 随机打乱 | Collections.shuffle(deck) |
swap(List, i, j) | 交换元素 | Collections.swap(list,0,1) |
rotate(List, distance) | 循环移动 | Collections.rotate(list, 2) |
fill(List, obj) | 全部填充 | Collections.fill(list, null) |
copy(dest, src) | 复制 | Collections.copy(dest, src) |
replaceAll(list, oldVal, newVal) | 替换 | Collections.replaceAll(list, 0, -1) |
binarySearch(List, key) | 二分查找 | Collections.binarySearch(list, 5) |
max(Collection), min(Collection) | 最大最小值 | Collections.max(set) |
frequency(Collection, obj) | 出现次数 | Collections.frequency(list, "A") |
disjoint(c1, c2) | 是否无交集 | Collections.disjoint(set1, set2) |
synchronizedXxx(collection) | 转为线程安全 | List<String> syncList = Collections.synchronizedList(new ArrayList<>()) |
unmodifiableXxx(collection) | 转为不可修改 | List<String> unmod = Collections.unmodifiableList(list) |
int compareTo(T o)。public class Employee implements Comparable<Employee> {
private int salary;
@Override
public int compareTo(Employee o) {
return Integer.compare(this.salary, o.salary);
}
}
int compare(T o1, T o2)。TreeSet、TreeMap、Collections.sort 等。Comparator<Employee> byName = (e1, e2) -> e1.getName().compareTo(e2.getName());
Comparator<Employee> bySalaryDesc = (e1, e2) -> Integer.compare(e2.getSalary(), e1.getSalary());
// 链式排序
Comparator<Employee> chain = Comparator.comparing(Employee::getDept)
.thenComparing(Employee::getSalary);
| 特性 | Comparable | Comparator |
|---|---|---|
| 位置 | 定义在类内部 | 定义在外部独立 |
| 修改类 | 需要修改原类 | 无需修改原类 |
| 排序方式 | 只能一种自然顺序 | 可多种自定义顺序 |
| 方法 | compareTo(T o) | compare(T o1, T o2) |
Properties props = new Properties();
// 从类路径加载
try (InputStream in = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("config.properties")) {
props.load(in);
}
// 从文件系统加载
try (FileInputStream fis = new FileInputStream("config.properties")) {
props.load(fis);
}
// 读取属性,提供默认值
String dbUrl = props.getProperty("db.url", "jdbc:mysql://localhost:3306/test");
props.setProperty("app.name", "MyApp");
try (FileOutputStream out = new FileOutputStream("config.properties")) {
props.store(out, "Application Configuration");
}
// 加载 XML props.loadFromXML(in); // 存储为 XML props.storeToXML(out, "Comment");
| 场景 | 推荐集合 |
|---|---|
| 存储顺序重要,允许重复 | ArrayList |
| 频繁在中间插入删除 | LinkedList |
| 去重,不关心顺序 | HashSet |
| 去重,需要保持插入顺序 | LinkedHashSet |
| 去重,需要自动排序 | TreeSet |
| 键值对,快速查找 | HashMap |
| 键值对,需要保持插入顺序 | LinkedHashMap |
| 键值对,需要按 Key 排序 | TreeMap |
| 线程安全的键值对 | ConcurrentHashMap |
| 读多写少的列表 | CopyOnWriteArrayList |
| 配置文件 | Properties |
(n-1) & hash 代替 % 运算,提高速度。Collections.synchronizedMap(new HashMap<>())ConcurrentHashMap(推荐)HashMap 的 Key 唯一性。Comparable 或构造时传入 Comparator。remove 会修改 modCount,导致迭代器检查失败抛出异常。class Person {
String name;
int age;
// 构造器、getter、toString 省略
}
List<Person> persons = Arrays.asList(
new Person("张三", 20),
new Person("李四", 18),
new Person("张三", 22)
);
persons.sort(Comparator.comparing(Person::getName)
.thenComparingInt(Person::getAge));
String text = "hello world hello java";
Map<String, Integer> freq = new HashMap<>();
for (String word : text.split(" ")) {
freq.merge(word, 1, Integer::sum);
}
System.out.println(freq); // {hello=2, world=1, java=1}
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;
public LRUCache(int maxSize) {
super(16, 0.75f, true);
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize;
}
}
ArrayList、LinkedList、HashMap、HashSet 的底层原理是面试和开发的基础。Comparable 和 Comparator,编写灵活的排序逻辑。ConcurrentHashMap 而非同步包装类。Collections 工具类简化代码。也可以深入学习 源码分析 和 并发集合,进一步理解 Java 容器的设计精髓。