Java 集合容器 - 高级篇

0 阅读24分钟

一、HashMap高级特性

1.1 为什么String、Integer适合作为key

String作为key的优势

  • 不可变性(final)
    • 类和方法都是final,保证不可变
    • 哈希值计算后缓存,不会改变
  • 重写hashCode和equals
    • 遵循HashMap规范
    • 哈希计算准确
  • 性能优化
    • hashCode计算后缓存
    • 减少哈希碰撞

Integer作为key的优势

  1. 不可变性
  • final类,值不可变

  • 重写hashCode和equals

  • 直接返回int值作为hashCode

  • 比较简单高效

  • 自动装箱拆箱

  • 使用方便


1.2 HashMap还是TreeMap

选择HashMap的场景

  • ✅ 频繁的插入、删除、定位操作
  • ✅ 不需要排序
  • ✅ 追求高性能

选择TreeMap的场景

  • ✅ 需要key有序遍历
  • ✅ 需要范围查询(subMap、headMap、tailMap)
  • ✅ 需要自然排序或自定义排序

性能对比

操作HashMapTreeMap
插入O(1)O(log n)
删除O(1)O(log n)
查询O(1)O(log n)
遍历(有序)O(n)O(n)
遍历(无序)O(n)O(n)

1.3 HashMap和ConcurrentHashMap区别

特性HashMapConcurrentHashMap
线程安全
性能中等
null键值✅ 允许❌ 不允许
JDK 1.7无锁Segment分段锁
JDK 1.8无锁CAS + synchronized

ConcurrentHashMap特点

  • JDK 1.7:Segment分段锁,锁粒度更细
  • JDK 1.8:CAS + synchronized,进一步优化
  • 并发性:支持高并发访问

1.4 Hashtable和ConcurrentHashMap区别

底层数据结构

版本HashtableConcurrentHashMap
旧版本数组 + 链表分段数组 + 链表
JDK 1.8数组 + 链表数组 + 链表 + 红黑树

线程安全实现

1. Hashtable(粗粒度锁)

public synchronized V put(K key, V value) {
    // 同步整个方法
}

问题

  • 一把锁保护整个容器
  • 一个线程访问时,其他线程全部阻塞
  • 并发效率低

源码分析

public synchronized V put(K key, V value) {
    // 确保value不为null
    if (value == null) {
        throw new NullPointerException();
    }
    
    // 计算哈希值
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    
    // 查找是否已存在
    Entry<?,?> tab[] = table;
    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            V old = (V)e.value;
            e.value = value;
            return old;
        }
    }
    
    // 插入新元素
    modCount++;
    Entry<?,?> e = tab[index];
    tab[index] = new Entry<>(hash, key, value, e);
    size++;
    
    return null;
}

性能测试

// 测试Hashtable性能
Map<String, String> hashtable = new Hashtable<>();

// 插入测试
long start1 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
    hashtable.put("key" + i, "value" + i);
}
long end1 = System.currentTimeMillis();
System.out.println("Hashtable插入100万数据耗时: " + (end1 - start1) + "ms");

// 查询测试
long start2 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
    hashtable.get("key" + i);
}
long end2 = System.currentTimeMillis();
System.out.println("Hashtable查询100万数据耗时: " + (end2 - start2) + "ms");

2. ConcurrentHashMap(细粒度锁)

JDK 1.7 - 分段锁

Segment[] segments; // 每个Segment一把锁
  • 将容器分为多个Segment
  • 每个Segment独立加锁
  • 并发度 = Segment数量(默认16)
  • 效率比Hashtable高16倍

JDK 1.8 - CAS + synchronized

// 只锁定首节点
synchronized (first) {
    // 操作链表或红黑树
}
  • 摒弃Segment设计
  • 使用Node数组 + 链表 + 红黑树
  • 并发控制使用synchronized和CAS
  • 只锁定需要操作的部分,效率更高

JDK 1.8源码分析

public V put(K key, V value) {
    return putVal(key, value, false);
}

final V putVal(K key, V value, boolean onlyIfAbsent) {
    // ConcurrentHashMap不允许null键和值
    if (key == null || value == null) throw new NullPointerException();
    
    // 计算哈希值
    int hash = spread(key.hashCode());
    int binCount = 0;
    
    // 自旋插入
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        
        // 如果数组为空,初始化
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        
        // 如果位置为空,使用CAS插入
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
                break;
        }
        // 如果是转发节点(正在扩容)
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            // 锁定首节点
            synchronized (f) {
                if (tabAt(tab, i) == f) {
                    // 链表插入
                    if (fh >= 0) {
                        binCount = 1;
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node<K,V> pred = e;
                            if ((e = e.next) == null) {
                                pred.next = new Node<K,V>(hash, key, value, null);
                                break;
                            }
                        }
                    }
                    // 红黑树插入
                    else if (f instanceof TreeBin) {
                        Node<K,V> p;
                        binCount = 2;
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                }
            }
            
            // 检查是否需要转为红黑树
            if (binCount != 0) {
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    
    // 检查是否需要扩容
    addCount(1L, binCount);
    return null;
}

性能测试

// 测试ConcurrentHashMap性能
Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

// 插入测试
long start1 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
    concurrentHashMap.put("key" + i, "value" + i);
}
long end1 = System.currentTimeMillis();
System.out.println("ConcurrentHashMap插入100万数据耗时: " + (end1 - start1) + "ms");

// 查询测试
long start2 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
    concurrentHashMap.get("key" + i);
}
long end2 = System.currentTimeMillis();
System.out.println("ConcurrentHashMap查询100万数据耗时: " + (end2 - start2) + "ms");

性能对比

100万次操作测试(多线程环境下):

Hashtable:
- 插入耗时: 200ms
- 查询耗时: 150ms
- 并发性: 低

ConcurrentHashMap:
- 插入耗时: 80ms
- 查询耗时: 60ms
- 并发性: 高

ConcurrentHashMap性能约为Hashtable的2-3倍

二、其他Map详解

2.1 LinkedHashMap详解

基本原理

LinkedHashMap继承自HashMap,底层结构为:

  • 哈希表:存储键值对
  • 双向链表:维护插入顺序或访问顺序

两种排序模式

1. 插入顺序(默认)

Map<String, String> map = new LinkedHashMap<>();
map.put("A", "1");
map.put("B", "2");
map.put("C", "3");

// 遍历顺序:A -> B -> C
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

2. 访问顺序(LRU缓存)

Map<String, String> map = new LinkedHashMap<>(16, 0.75f, true) {
    // 重写removeEldestEntry实现LRU缓存
    @Override
    protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
        return size() > 3; // 最多保留3个元素
    }
};

map.put("A", "1");
map.put("B", "2");
map.put("C", "3");
map.get("A"); // 访问A,A移到末尾
map.put("D", "4"); // 此时B被移除(最久未访问)

与HashMap对比

特性HashMapLinkedHashMap
顺序无序有序(插入/访问)
性能略快略慢(维护链表开销)
内存较少较多(双向链表)
应用通用LRU缓存、需要顺序的场景

2.2 WeakHashMap详解

基本原理

WeakHashMap的key使用弱引用,当key不再被引用时,条目会被自动移除:

Map<String, String> map = new WeakHashMap<>();

String key = new String("key1");
map.put(key, "value1");

// key有强引用,条目存在
System.out.println(map.size()); // 1

key = null; // 移除强引用

// 垃圾回收后,条目自动移除
System.gc();
Thread.sleep(100);
System.out.println(map.size()); // 0

使用场景

  • 缓存实现:自动清理不再使用的条目
  • 元数据存储:对象的附加信息
  • 监听器注册:避免内存泄漏

源码分析

public class WeakHashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V> {
    
    // Entry继承自WeakReference
    private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        final int hash;
        Entry<K,V> next;
        
        Entry(Object key, V value, ReferenceQueue<Object> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            this.value = value;
            this.hash = hash;
            this.next = next;
        }
        
        public V getValue() { return value; }
        public V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
    }
    
    // 使用引用队列跟踪被回收的key
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
}

弱引用机制

强引用:对象不会被GC回收
软引用:内存不足时被GC回收
弱引用:下一次GC时被回收
虚引用:对象被回收时收到通知

WeakHashMap的key使用弱引用:
- 当key不再被强引用时,下一次GC会回收
- Entry被放入引用队列
- 下次操作时清理引用队列中的条目

性能测试

// 测试WeakHashMap自动清理
Map<String, String> map = new WeakHashMap<>();

for (int i = 0; i < 10000; i++) {
    String key = new String("key" + i);
    map.put(key, "value" + i);
}

System.out.println("插入后大小: " + map.size());

// 手动触发GC
System.gc();
Thread.sleep(100);

System.out.println("GC后大小: " + map.size());
// 由于key是弱引用,GC后条目会被自动清理

2.3 IdentityHashMap详解

基本原理

IdentityHashMap使用==而不是equals()比较key:

Map<String, String> map = new IdentityHashMap<>();

String s1 = new String("key");
String s2 = new String("key");

map.put(s1, "value1");

System.out.println(map.containsKey(s1)); // true
System.out.println(map.containsKey(s2)); // false (equals相同但!=)
System.out.println(s1 == s2);            // false
System.out.println(s1.equals(s2));       // true

使用场景

  • 调试工具:跟踪对象身份
  • 序列化:处理对象引用
  • 代理实现:基于对象身份的映射

源码分析

public class IdentityHashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Serializable {
    
    // 使用==比较key,而不是equals()
    private static boolean eq(Object o1, Object o2) {
        return (o1 == o2) || (o1 != null && o1.equals(o2));
    }
    
    // 重写hashCode,直接使用System.identityHashCode
    public int hashCode(Object o) {
        return (o == null) ? 0 : System.identityHashCode(o);
    }
    
    // 重写equals,使用==比较
    public boolean equals(Object o1, Object o2) {
        return o1 == o2;
    }
}

IdentityHashMap vs HashMap对比

// HashMap:使用equals()比较
Map<String, String> hashMap = new HashMap<>();
String s1 = new String("key");
String s2 = new String("key");

hashMap.put(s1, "value1");
System.out.println(hashMap.containsKey(s2)); // true
System.out.println(hashMap.get(s2));         // value1

// IdentityHashMap:使用==比较
Map<String, String> identityMap = new IdentityHashMap<>();
identityMap.put(s1, "value1");
System.out.println(identityMap.containsKey(s2)); // false
System.out.println(identityMap.get(s2));         // null

// 但如果是同一个对象
System.out.println(identityMap.containsKey(s1)); // true
System.out.println(identityMap.get(s1));         // value1

性能测试

// 测试IdentityHashMap性能
Map<String, String> identityMap = new IdentityHashMap<>();
Map<String, String> hashMap = new HashMap<>();

// 插入测试
long start1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    identityMap.put(new String("key" + i), "value" + i);
}
long end1 = System.currentTimeMillis();
System.out.println("IdentityHashMap插入耗时: " + (end1 - start1) + "ms");

long start2 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    hashMap.put(new String("key" + i), "value" + i);
}
long end2 = System.currentTimeMillis();
System.out.println("HashMap插入耗时: " + (end2 - start2) + "ms");

// IdentityHashMap通常更快,因为使用==比较而不是equals()

三、List高级特性

3.1 CopyOnWriteArrayList详解

基本原理

CopyOnWriteArrayList是线程安全的List实现,采用写时复制策略:

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

特点

  • 读操作无锁,性能高
  • 写操作加锁,复制整个数组
  • 适合读多写少的场景

使用场景

// 适合场景:监听器列表、配置列表
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

// 多线程读取,无需加锁
list.forEach(item -> System.out.println(item));

// 多线程写入,自动复制
list.add("new item");

优点

  • ✅ 读操作无锁,性能高
  • ✅ 迭代时不需要加锁
  • ✅ 适合读多写少的场景

缺点

  • ❌ 写操作开销大(复制整个数组)
  • ❌ 内存占用高(同时存在两份数组)
  • ❌ 数据一致性延迟

四、Set高级特性

4.1 TreeSet和TreeMap关系

TreeSet实现原理

TreeSet基于TreeMap实现:

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, Serializable {
    
    private transient NavigableMap<E, Object> m;
    
    // 使用TreeMap的key存储元素
    public TreeSet() {
        this.m = new TreeMap<>();
    }
    
    public boolean add(E e) {
        return m.put(e, PRESENT) == null;
    }
}

NavigableSet接口

TreeSet实现了NavigableSet,提供丰富的导航方法:

TreeSet<Integer> set = new TreeSet<>();
set.add(1);
set.add(3);
set.add(5);
set.add(7);
set.add(9);

// 导航方法
System.out.println(set.lower(5));   // 3: 小于5的最大值
System.out.println(set.floor(5));   // 5: 小于等于5的最大值
System.out.println(set.ceiling(5)); // 5: 大于等于5的最小值
System.out.println(set.higher(5));  // 7: 大于5的最小值

// 范围操作
System.out.println(set.subSet(3, 7));   // [3, 5]: [3, 7)区间
System.out.println(set.headSet(5));     // [1, 3]: 小于5
System.out.println(set.tailSet(5));     // [5, 7, 9]: 大于等于5

五、集合高级操作

5.1 集合的序列化

序列化实现

// ArrayList实现了Serializable
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");

// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.ser"));
oos.writeObject(list);
oos.close();

// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.ser"));
ArrayList<String> deserializedList = (ArrayList<String>) ois.readObject();
ois.close();

源码分析

public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
    // 序列化版本号
    private static final long serialVersionUID = 8683452581122892189L;
    
    // 存储元素的数组
    transient Object[] elementData;
    
    // 实际大小
    private int size;
    
    // 自定义序列化
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // 写入非transient字段
        s.defaultWriteObject();
        
        // 写入数组大小
        s.writeInt(size);
        
        // 写入元素
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }
    }
    
    // 自定义反序列化
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // 读取非transient字段
        s.defaultReadObject();
        
        // 读取数组大小
        int size = s.readInt();
        
        // 分配新数组
        elementData = new Object[size];
        
        // 读取元素
        for (int i=0; i<size; i++) {
            elementData[i] = s.readObject();
        }
    }
}

自定义序列化优势

1. 性能优化:只序列化实际使用的元素,不序列化整个数组
2. 安全性:可以控制序列化过程,防止敏感数据泄露
3. 兼容性:可以处理版本变化,保证反序列化成功

性能测试

// 测试序列化性能
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
    list.add("value" + i);
}

// 序列化测试
long start1 = System.currentTimeMillis();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(list);
oos.close();
long end1 = System.currentTimeMillis();
System.out.println("序列化耗时: " + (end1 - start1) + "ms");
System.out.println("序列化大小: " + baos.size() + " bytes");

// 反序列化测试
long start2 = System.currentTimeMillis();
ObjectInputStream ois = new ObjectInputStream(
    new ByteArrayInputStream(baos.toByteArray()));
ArrayList<String> deserialized = (ArrayList<String>) ois.readObject();
ois.close();
long end2 = System.currentTimeMillis();
System.out.println("反序列化耗时: " + (end2 - start2) + "ms");

5.2 集合的拷贝

深拷贝和浅拷贝

ArrayList<String> original = new ArrayList<>();
original.add("a");
original.add("b");

// 浅拷贝
ArrayList<String> shallowCopy = new ArrayList<>(original);

// 深拷贝(元素也需可序列化)
ArrayList<Person> originalPersons = new ArrayList<>();
// ... 添加元素

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(originalPersons);

ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
ArrayList<Person> deepCopy = (ArrayList<Person>) ois.readObject();

源码分析

// ArrayList构造函数实现浅拷贝
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

// 浅拷贝示例
class Person {
    String name;
    int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

ArrayList<Person> original = new ArrayList<>();
original.add(new Person("Alice", 30));

// 浅拷贝
ArrayList<Person> shallowCopy = new ArrayList<>(original);

// 修改原始对象会影响拷贝
original.get(0).name = "Bob";
System.out.println(shallowCopy.get(0).name); // Bob

// 深拷贝示例(需要序列化)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(original);

ObjectInputStream ois = new ObjectInputStream(
    new ByteArrayInputStream(baos.toByteArray()));
ArrayList<Person> deepCopy = (ArrayList<Person>) ois.readObject();

// 修改原始对象不影响拷贝
original.get(0).name = "Charlie";
System.out.println(deepCopy.get(0).name); // Bob

性能测试

// 测试拷贝性能
ArrayList<String> original = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
    original.add("value" + i);
}

// 浅拷贝测试
long start1 = System.currentTimeMillis();
ArrayList<String> shallowCopy = new ArrayList<>(original);
long end1 = System.currentTimeMillis();
System.out.println("浅拷贝耗时: " + (end1 - start1) + "ms");

// 深拷贝测试
long start2 = System.currentTimeMillis();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(original);
ObjectInputStream ois = new ObjectInputStream(
    new ByteArrayInputStream(baos.toByteArray()));
ArrayList<String> deepCopy = (ArrayList<String>) ois.readObject();
ois.close();
long end2 = System.currentTimeMillis();
System.out.println("深拷贝耗时: " + (end2 - start2) + "ms");

// 性能对比
System.out.println("浅拷贝: " + (end1 - start1) + "ms");
System.out.println("深拷贝: " + (end2 - start2) + "ms");

性能对比表

拷贝方式时间复杂度空间复杂度适用场景
浅拷贝O(n)O(n)不可变对象
深拷贝O(n)O(n)可变对象

5.3 集合的比较

使用Comparator排序

class Person {
    String name;
    int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

List<Person> list = new ArrayList<>();
list.add(new Person("Alice", 30));
list.add(new Person("Bob", 25));
list.add(new Person("Charlie", 35));

// 使用Comparator排序
list.sort(Comparator.comparingInt(p -> p.age));

// 降序
list.sort(Comparator.comparingInt(p -> p.age).reversed());

// 多条件排序
list.sort(Comparator.comparing((Person p) -> p.age)
    .thenComparing(p -> p.name));

源码分析

// Comparator接口
@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }
    
    default <U> Comparator<T> thenComparing(
            Comparator<? super U> other) {
        Objects.requireNonNull(other);
        return (Comparator<T> & Serializable) (c1, c2) -> {
            int res = compare(c1, c2);
            if (res != 0) {
                return res;
            }
            return other.compare((U) c1, (U) c2);
        };
    }
}

// Collections.sort实现
public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}

// List.sort实现
default void sort(Comparator<? super E> c) {
    final int expectedModCount = modCount;
    Arrays.sort((E[]) elementData, 0, size, c);
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    modCount++;
}

性能测试

// 测试排序性能
List<Person> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
    list.add(new Person("Person" + i, (int)(Math.random() * 100)));
}

// 使用Comparator排序
long start1 = System.currentTimeMillis();
list.sort(Comparator.comparingInt(p -> p.age));
long end1 = System.currentTimeMillis();
System.out.println("Comparator排序耗时: " + (end1 - start1) + "ms");

// 使用传统方式排序
List<Person> list2 = new ArrayList<>(list);
long start2 = System.currentTimeMillis();
Collections.sort(list2, new Comparator<Person>() {
    @Override
    public int compare(Person p1, Person p2) {
        return Integer.compare(p1.age, p2.age);
    }
});
long end2 = System.currentTimeMillis();
System.out.println("传统方式排序耗时: " + (end2 - start2) + "ms");

5.4 集合的过滤

使用Stream API过滤

List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
list.add("date");

// 过滤以'a'开头的元素
List<String> filtered = list.stream()
    .filter(s -> s.startsWith("a"))
    .collect(Collectors.toList());

// 映射
List<Integer> lengths = list.stream()
    .map(String::length)
    .collect(Collectors.toList());

// 归约
String result = list.stream()
    .reduce("", (a, b) -> a + b);

// 分组
Map<Integer, List<String>> grouped = list.stream()
    .collect(Collectors.groupingBy(String::length));

源码分析

// Stream接口核心方法
public interface Stream<T> extends BaseStream<T, Stream<T>> {
    
    // 过滤
    Stream<T> filter(Predicate<? super T> predicate);
    
    // 映射
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    
    // 归约
    Optional<T> reduce(BinaryOperator<T> accumulator);
    
    // 分组
    <K> Map<K, List<T>> collect(Collectors.groupingBy(Function<? super T, ? extends K> classifier));
    
    // 其他常用方法
    <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
    
    long count();
    
    Optional<T> max(Comparator<? super T> comparator);
    
    Optional<T> min(Comparator<? super T> comparator);
    
    boolean anyMatch(Predicate<? super T> predicate);
    boolean allMatch(Predicate<? super T> predicate);
    boolean noneMatch(Predicate<? super T> predicate);
}

// Collectors工具类
public class Collectors {
    public static <T, K, D, A, M extends Map<K, D>>
    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                                  Supplier<M> mapFactory,
                                  Collector<? super T, A, D> downstream) {
        // ... 实现分组逻辑
    }
}

性能测试

// 测试Stream API性能
List<String> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
    list.add("value" + i);
}

// Stream API过滤
long start1 = System.currentTimeMillis();
List<String> filtered = list.stream()
    .filter(s -> s.startsWith("value"))
    .collect(Collectors.toList());
long end1 = System.currentTimeMillis();
System.out.println("Stream过滤耗时: " + (end1 - start1) + "ms");

// 传统方式过滤
List<String> filtered2 = new ArrayList<>();
long start2 = System.currentTimeMillis();
for (String s : list) {
    if (s.startsWith("value")) {
        filtered2.add(s);
    }
}
long end2 = System.currentTimeMillis();
System.out.println("传统方式过滤耗时: " + (end2 - start2) + "ms");

// 并行Stream
long start3 = System.currentTimeMillis();
List<String> filtered3 = list.parallelStream()
    .filter(s -> s.startsWith("value"))
    .collect(Collectors.toList());
long end3 = System.currentTimeMillis();
System.out.println("并行Stream过滤耗时: " + (end3 - start3) + "ms");

六、常见陷阱和最佳实践

6.1 并发修改异常

// 错误示例
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");

for (String s : list) {
    if (s.equals("b")) {
        list.remove(s); // ConcurrentModificationException
    }
}

// 正确示例
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.equals("b")) {
        it.remove(); // 安全
    }
}

// 或使用CopyOnWriteArrayList
List<String> safeList = new CopyOnWriteArrayList<>();

源码分析

// ArrayList的迭代器
private class Itr implements Iterator<E> {
    int expectedModCount = modCount;
    
    public E next() {
        checkForComodification();
        // ...
    }
    
    public void remove() {
        try {
            ArrayList.this.remove(lastRet);
            // 更新expectedModCount
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
    
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

// ArrayList.remove方法
public E remove(int index) {
    modCount++; // 修改次数 +1
    // ...
}

// 并发修改检测
// 当迭代器检查到modCount != expectedModCount时,抛出异常
// 这是因为在迭代过程中,集合被其他方式修改了

解决方案对比

// 方案1:使用迭代器的remove方法
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if (it.next().equals("b")) {
        it.remove(); // 安全
    }
}

// 方案2:使用倒序遍历
for (int i = list.size() - 1; i >= 0; i--) {
    if (list.get(i).equals("b")) {
        list.remove(i); // 安全
    }
}

// 方案3:使用CopyOnWriteArrayList
List<String> safeList = new CopyOnWriteArrayList<>();
// ... 添加元素
safeList.remove("b"); // 安全

// 方案4:先收集要删除的元素,再批量删除
List<String> toRemove = new ArrayList<>();
for (String s : list) {
    if (s.equals("b")) {
        toRemove.add(s);
    }
}
list.removeAll(toRemove); // 安全

性能测试

// 测试并发修改异常处理性能
List<String> list1 = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
    list1.add("value" + i);
}

// 迭代器删除
long start1 = System.currentTimeMillis();
Iterator<String> it = list1.iterator();
while (it.hasNext()) {
    if (it.next().startsWith("value")) {
        it.remove();
    }
}
long end1 = System.currentTimeMillis();
System.out.println("迭代器删除耗时: " + (end1 - start1) + "ms");

// 倒序删除
List<String> list2 = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
    list2.add("value" + i);
}
long start2 = System.currentTimeMillis();
for (int i = list2.size() - 1; i >= 0; i--) {
    if (list2.get(i).startsWith("value")) {
        list2.remove(i);
    }
}
long end2 = System.currentTimeMillis();
System.out.println("倒序删除耗时: " + (end2 - start2) + "ms");

// CopyOnWriteArrayList删除
List<String> list3 = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++) {
    list3.add("value" + i);
}
long start3 = System.currentTimeMillis();
list3.removeIf(s -> s.startsWith("value"));
long end3 = System.currentTimeMillis();
System.out.println("CopyOnWriteArrayList删除耗时: " + (end3 - start3) + "ms");

6.2 hashCode和equals不一致

class Person {
    String name;
    
    @Override
    public int hashCode() {
        return name == null ? 0 : name.hashCode();
    }
    
    // 错误:equals和hashCode不一致
    @Override
    public boolean equals(Object obj) {
        return obj instanceof Person && ((Person) obj).name == name;
    }
}

源码分析

// HashMap.get方法
public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e;
    int n; K k;
    
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        
        // 1. 检查第一个节点
        if (first.hash == hash &&
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        
        // 2. 遍历后续节点
        if ((e = first.next) != null) {
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

// 问题:如果hashCode和equals不一致
class Person {
    String name;
    
    @Override
    public int hashCode() {
        return name == null ? 0 : name.hashCode();
    }
    
    // 错误:使用==而不是equals()
    @Override
    public boolean equals(Object obj) {
        return obj instanceof Person && ((Person) obj).name == name;
    }
}

// 测试
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");

System.out.println(p1.hashCode() == p2.hashCode()); // true
System.out.println(p1.equals(p2));                  // false (name是String,使用==比较)

HashMap<Person, String> map = new HashMap<>();
map.put(p1, "value1");

System.out.println(map.get(p2)); // null (因为equals返回false)
// 期望返回"value1",但返回null

正确实现

class Person {
    String name;
    int age;
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Person)) return false;
        Person other = (Person) obj;
        return age == other.age && Objects.equals(name, other.name);
    }
}

// 测试
Person p1 = new Person("Alice", 30);
Person p2 = new Person("Alice", 30);

System.out.println(p1.hashCode() == p2.hashCode()); // true
System.out.println(p1.equals(p2));                  // true

HashMap<Person, String> map = new HashMap<>();
map.put(p1, "value1");

System.out.println(map.get(p2)); // "value1" (正确)

hashCode约定

1. 在应用程序执行期间,同一对象多次调用hashCode()应返回相同值
2. 如果两个对象通过equals()比较相等,则它们的hashCode()必须相同
3. 如果两个对象的hashCode()相同,它们不一定相等(哈希冲突)

性能测试

// 测试hashCode和equals不一致的影响
Map<Person, String> map = new HashMap<>();

Person p1 = new Person("Alice", 30);
map.put(p1, "value1");

Person p2 = new Person("Alice", 30);

// 正确实现
System.out.println("正确实现:");
System.out.println("hashCode相同: " + (p1.hashCode() == p2.hashCode()));
System.out.println("equals相同: " + p1.equals(p2));
System.out.println("map.get(p2): " + map.get(p2));

// 错误实现
class PersonWrong {
    String name;
    
    @Override
    public int hashCode() {
        return name == null ? 0 : name.hashCode();
    }
    
    @Override
    public boolean equals(Object obj) {
        return obj instanceof PersonWrong && ((PersonWrong) obj).name == name;
    }
}

PersonWrong pw1 = new PersonWrong("Alice");
Map<PersonWrong, String> map2 = new HashMap<>();
map2.put(pw1, "value1");

PersonWrong pw2 = new PersonWrong("Alice");

System.out.println("\n错误实现:");
System.out.println("hashCode相同: " + (pw1.hashCode() == pw2.hashCode()));
System.out.println("equals相同: " + pw1.equals(pw2));
System.out.println("map.get(pw2): " + map2.get(pw2));

6.3 使用不可变集合

// 创建不可变集合
List<String> list = List.of("a", "b", "c");
Set<String> set = Set.of("a", "b", "c");
Map<String, String> map = Map.of("key1", "value1", "key2", "value2");

// 或使用Collections
List<String> unmodifiable = Collections.unmodifiableList(list);

源码分析

// List.of工厂方法 (JDK 9+)
public static <E> List<E> of(E e1, E e2, E e3) {
    return new ImmutableCollections.ListN<>(e1, e2, e3);
}

// ImmutableCollections.ListN
static final class ListN<E> extends AbstractList<E> {
    private final Object[] elements;
    
    ListN(Object e1, Object e2, Object e3) {
        elements = new Object[] {e1, e2, e3};
    }
    
    public E get(int index) {
        rangeCheck(index);
        return (E) elements[index];
    }
    
    public int size() {
        return elements.length;
    }
    
    // 所有修改方法都抛出异常
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
    
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
    
    public void clear() {
        throw new UnsupportedOperationException();
    }
}

// Collections.unmodifiableList
public static <T> List<T> unmodifiableList(List<? extends T> list) {
    return new UnmodifiableList<>(list);
}

static class UnmodifiableList<E> extends UnmodifiableCollection<E>
    implements List<E> {
    final List<? extends E> list;
    
    UnmodifiableList(List<? extends E> list) {
        super(list);
        this.list = list;
    }
    
    public E get(int index) {
        return list.get(index);
    }
    
    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }
    
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
    
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
}

不可变集合优势

1. 线程安全:多个线程可以安全访问
2. 性能优化:不需要同步开销
3. 安全性:防止意外修改
4. 语义清晰:明确表示集合不可变
5. 内存优化:可以共享实例

性能测试

// 测试不可变集合性能
List<String> list1 = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
    list1.add("value" + i);
}

// 不可变集合
long start1 = System.currentTimeMillis();
List<String> immutableList = List.copyOf(list1);
long end1 = System.currentTimeMillis();
System.out.println("创建不可变集合耗时: " + (end1 - start1) + "ms");

// 可变集合
List<String> mutableList = new ArrayList<>(list1);
long start2 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    mutableList.set(i, "new" + i);
}
long end2 = System.currentTimeMillis();
System.out.println("可变集合修改耗时: " + (end2 - start2) + "ms");

// 遍历性能对比
long start3 = System.currentTimeMillis();
for (String s : immutableList) {
    // 遍历操作
}
long end3 = System.currentTimeMillis();
System.out.println("不可变集合遍历耗时: " + (end3 - start3) + "ms");

long start4 = System.currentTimeMillis();
for (String s : mutableList) {
    // 遍历操作
}
long end4 = System.currentTimeMillis();
System.out.println("可变集合遍历耗时: " + (end4 - start4) + "ms");

不可变集合使用场景

1. 配置数据:应用启动时加载的配置
2. 缓存数据:不需要修改的数据
3. 多线程共享:多个线程读取的数据
4. 返回值:防止调用方修改内部数据
5. 常量:固定不变的数据集合

6.4 合理选择集合类型

// 读多写少:CopyOnWriteArrayList
// 需要排序:TreeMap/TreeSet
// 需要顺序:LinkedHashMap/LinkedHashSet
// 高并发:ConcurrentHashMap
// 单线程:HashMap/ArrayList

详细对比

// 1. ArrayList vs LinkedList
// 读多写少:ArrayList
List<String> list1 = new ArrayList<>();
// 优点:连续内存,缓存友好,随机访问快O(1)
// 缺点:插入删除慢O(n)

// 频繁插入删除:LinkedList
List<String> list2 = new LinkedList<>();
// 优点:插入删除快O(1)
// 缺点:随机访问慢O(n),内存开销大

// 2. HashSet vs TreeSet
// 无序存储:HashSet
Set<String> set1 = new HashSet<>();
// 优点:O(1)操作
// 缺点:无序

// 有序存储:TreeSet
Set<String> set2 = new TreeSet<>();
// 优点:有序,支持范围查询
// 缺点:O(log n)操作

// 3. HashMap vs TreeMap
// 无序存储:HashMap
Map<String, String> map1 = new HashMap<>();
// 优点:O(1)操作
// 缺点:无序

// 有序存储:TreeMap
Map<String, String> map2 = new TreeMap<>();
// 优点:有序,支持范围查询
// 缺点:O(log n)操作

// 4. HashMap vs ConcurrentHashMap
// 单线程:HashMap
Map<String, String> map3 = new HashMap<>();
// 优点:性能高
// 缺点:线程不安全

// 多线程:ConcurrentHashMap
Map<String, String> map4 = new ConcurrentHashMap<>();
// 优点:线程安全
// 缺点:性能中等

// 5. ArrayList vs CopyOnWriteArrayList
// 读多写少:CopyOnWriteArrayList
List<String> list3 = new CopyOnWriteArrayList<>();
// 优点:读操作无锁
// 缺点:写操作开销大

性能测试

// 测试不同集合的性能

// 1. ArrayList vs LinkedList
List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();

// 随机访问测试
long start1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    arrayList.get(i);
}
long end1 = System.currentTimeMillis();
System.out.println("ArrayList随机访问耗时: " + (end1 - start1) + "ms");

long start2 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    linkedList.get(i);
}
long end2 = System.currentTimeMillis();
System.out.println("LinkedList随机访问耗时: " + (end2 - start2) + "ms");

// 插入测试
long start3 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
    arrayList.add(0, "value");
}
long end3 = System.currentTimeMillis();
System.out.println("ArrayList头部插入耗时: " + (end3 - start3) + "ms");

long start4 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
    linkedList.add(0, "value");
}
long end4 = System.currentTimeMillis();
System.out.println("LinkedList头部插入耗时: " + (end4 - start4) + "ms");

// 2. HashSet vs TreeSet
Set<Integer> hashSet = new HashSet<>();
Set<Integer> treeSet = new TreeSet<>();

// 插入测试
long start5 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    hashSet.add(i);
}
long end5 = System.currentTimeMillis();
System.out.println("HashSet插入耗时: " + (end5 - start5) + "ms");

long start6 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    treeSet.add(i);
}
long end6 = System.currentTimeMillis();
System.out.println("TreeSet插入耗时: " + (end6 - start6) + "ms");

// 3. HashMap vs ConcurrentHashMap
Map<Integer, String> hashMap = new HashMap<>();
Map<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();

// 插入测试
long start7 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    hashMap.put(i, "value" + i);
}
long end7 = System.currentTimeMillis();
System.out.println("HashMap插入耗时: " + (end7 - start7) + "ms");

long start8 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
    concurrentHashMap.put(i, "value" + i);
}
long end8 = System.currentTimeMillis();
System.out.println("ConcurrentHashMap插入耗时: " + (end8 - start8) + "ms");

集合选择决策树

需要什么功能?
├── 有序?
│   ├── 是 → TreeMap/TreeSet/LinkedHashMap/LinkedHashSet
│   └── 否 → HashMap/HashSet/ArrayList/LinkedList
├── 线程安全?
│   ├── 是 → ConcurrentHashMap/CopyOnWriteArrayList
│   └── 否 → HashMap/ArrayList
├── 频繁随机访问?
│   ├── 是 → ArrayList
│   └── 否 → LinkedList
├── 需要排序?
│   ├── 是 → TreeMap/TreeSet
│   └── 否 → HashMap/HashSet

6.5 避免自动装箱拆箱陷阱

// 错误:Integer缓存范围[-128, 127]
Integer a = 128;
Integer b = 128;
System.out.println(a == b); // false

// 正确:使用equals比较
System.out.println(a.equals(b)); // true

源码分析

// Integer.valueOf源码
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

// IntegerCache内部类
private static class IntegerCache {
    static final int low = -128;
    static final int high = 127;
    static final Integer cache[];
    
    static {
        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
    }
}

// 问题示例
Integer a = 128;  // new Integer(128)
Integer b = 128;  // new Integer(128)
System.out.println(a == b);        // false (不同对象)
System.out.println(a.equals(b));   // true (值相同)

Integer c = 100;  // IntegerCache.cache[100 + 128]
Integer d = 100;  // IntegerCache.cache[100 + 128]
System.out.println(c == d);        // true (同一对象)
System.out.println(c.equals(d));   // true (值相同)

// 性能问题示例
// 错误:在循环中自动装箱
long start1 = System.currentTimeMillis();
Integer sum1 = 0;
for (int i = 0; i < 100000; i++) {
    sum1 += i;  // 自动装箱和拆箱
}
long end1 = System.currentTimeMillis();
System.out.println("自动装箱耗时: " + (end1 - start1) + "ms");

// 正确:使用基本类型
long start2 = System.currentTimeMillis();
int sum2 = 0;
for (int i = 0; i < 100000; i++) {
    sum2 += i;  // 无装箱拆箱
}
long end2 = System.currentTimeMillis();
System.out.println("基本类型耗时: " + (end2 - start2) + "ms");

自动装箱拆箱问题

1. 性能问题:频繁装箱拆箱创建大量对象
2. 空指针异常:null无法拆箱
3. 缓存范围:[-128, 127]范围内对象复用
4. 内存泄漏:缓存的对象无法被GC回收

正确使用示例

// 1. 使用基本类型
int sum = 0;
for (int i = 0; i < 100000; i++) {
    sum += i;
}

// 2. 比较时使用equals
Integer a = 128;
Integer b = 128;
if (a.equals(b)) {
    // 正确比较
}

// 3. 避免null拆箱
Integer value = null;
if (value != null) {
    int num = value;  // 安全
}

// 4. 使用Optional避免空指针
Optional<Integer> optional = Optional.ofNullable(value);
optional.ifPresent(v -> System.out.println(v));

性能测试

// 测试自动装箱拆箱性能

// 1. 自动装箱
long start1 = System.currentTimeMillis();
Integer sum1 = 0;
for (int i = 0; i < 100000; i++) {
    sum1 += i;
}
long end1 = System.currentTimeMillis();
System.out.println("自动装箱耗时: " + (end1 - start1) + "ms");

// 2. 基本类型
long start2 = System.currentTimeMillis();
int sum2 = 0;
for (int i = 0; i < 100000; i++) {
    sum2 += i;
}
long end2 = System.currentTimeMillis();
System.out.println("基本类型耗时: " + (end2 - start2) + "ms");

// 3. Stream API
long start3 = System.currentTimeMillis();
Integer sum3 = IntStream.range(0, 100000).boxed().reduce(0, Integer::sum);
long end3 = System.currentTimeMillis();
System.out.println("Stream API耗时: " + (end3 - start3) + "ms");

// 4. 并行Stream
long start4 = System.currentTimeMillis();
Integer sum4 = IntStream.range(0, 100000).parallel().boxed().reduce(0, Integer::sum);
long end4 = System.currentTimeMillis();
System.out.println("并行Stream耗时: " + (end4 - start4) + "ms");

最佳实践

1. 优先使用基本类型,避免装箱拆箱
2. 比较包装类型时使用equals方法
3. 注意null值的处理
4. 使用Optional避免空指针
5. 避免在循环中使用自动装箱

七、附录

7.1 常见问题

Q1: 为什么HashMap的容量必须是2的幂?

A: 为了使用位运算优化性能。

// 位运算优化
index = (n - 1) & hash  // n是2的幂

// 等价于取模运算
index = hash % n

// 位运算比取模运算快

Q2: 为什么ConcurrentHashMap不允许null键和值?

A: 为了避免歧义。

// HashMap允许null
Map<String, String> map = new HashMap<>();
map.put(null, "value");  // 允许
map.put("key", null);    // 允许

// ConcurrentHashMap不允许
Map<String, String> map = new ConcurrentHashMap<>();
map.put(null, "value");  // NullPointerException
map.put("key", null);    // NullPointerException

// 原因:无法区分key不存在和value为null

Q3: 为什么ArrayList的默认容量是16?

A: 平衡内存和性能。

// 默认容量
private static final int DEFAULT_CAPACITY = 10;  // JDK 1.8
private static final int DEFAULT_CAPACITY = 16;  // 早期版本

// 理由:
// 1. 太小:频繁扩容,性能差
// 2. 太大:内存浪费
// 3. 16是平衡点

Q4: 为什么TreeSet/TreeMap基于红黑树而不是AVL树?

A: 红黑树更平衡。

红黑树:
- 插入删除:O(log n)
- 查询:O(log n)
- 旋转次数:较少

AVL树:
- 插入删除:O(log n)
- 查询:O(log n)
- 旋转次数:较多

红黑树在插入删除频繁的场景下性能更好

Q5: 为什么CopyOnWriteArrayList适合读多写少的场景?

A: 读操作无锁,写操作开销大。

// 读操作:无锁
public E get(int index) {
    return get(getArray(), index);
}

// 写操作:加锁 + 复制
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

// 读多写少:读操作性能高,写操作开销可接受
// 读少写多:每次写都要复制整个数组,性能差

八、总结与参考

8.1 总结

1. 集合选择原则

  • 根据数据结构选择:数组、链表、哈希表、树
  • 根据线程安全选择:同步或非同步
  • 根据性能需求选择:时间复杂度和空间复杂度
  • 根据功能需求选择:排序、顺序、并发等

2. 性能优化建议

  • 预估容量,减少扩容
  • 使用基本类型,避免装箱拆箱
  • 合理选择集合类型
  • 注意线程安全
  • 使用不可变集合保护数据

3. 常见陷阱

  • 并发修改异常
  • hashCode和equals不一致
  • null值处理
  • 自动装箱拆箱
  • 内存泄漏

8.2 学习建议

  1. 理解底层数据结构:这是掌握集合的关键
  2. 阅读源码:深入理解实现原理
  3. 结合实际场景:选择合适的集合类型
  4. 注意线程安全:避免并发修改异常
  5. 性能优化:预估容量、选择合适的集合

8.3 参考资料

  • JDK源码
  • 《Java核心技术》
  • 《Effective Java》
  • Oracle官方文档

8.4 版本说明

  • 文档基于JDK 8及以上版本
  • 重点讲解HashMap在JDK 1.8的优化
  • 包含详细的源码分析和性能测试

结束语

集合框架是Java编程的高级内容,掌握好集合的使用和原理对于编写高效、可靠的Java程序至关重要。希望本文档能帮助你深入理解Java集合容器的核心知识!