一、Java集合框架概述

Java集合框架就像是一个超级工具箱,里面装满了各种存放数据的容器。这些容器有的像数组但更灵活,有的像链表但更强大。我们今天要重点聊的是其中最常用的两对组合:ArrayList和LinkedList,HashMap和TreeMap。

想象一下你要整理衣柜。ArrayList就像把衣服一件件叠好放进抽屉,LinkedList则是把衣服用衣架串起来挂好。而HashMap和TreeMap更像是智能衣柜,能根据标签快速找到你想要的那件衣服。

// 技术栈:Java 11
import java.util.*;

public class CollectionDemo {
    public static void main(String[] args) {
        // ArrayList示例
        List<String> arrayList = new ArrayList<>();
        arrayList.add("第一件衣服");  // 添加到末尾
        arrayList.add(0, "第二件衣服");  // 添加到指定位置
        System.out.println("ArrayList内容: " + arrayList);
        
        // LinkedList示例
        List<String> linkedList = new LinkedList<>();
        linkedList.add("衣架上的第一件");
        linkedList.addFirst("新加的衣架衣服");  // 特有方法
        System.out.println("LinkedList内容: " + linkedList);
    }
}

二、ArrayList与LinkedList深度对比

ArrayList底层是个动态数组,就像自动扩容的储物箱。当箱子满了,它会悄悄换个大箱子,把东西全搬过去。而LinkedList则是双向链表,每个元素都记住前后邻居的位置。

随机访问性能:

// 技术栈:Java 11
public class RandomAccessTest {
    public static void main(String[] args) {
        List<Integer> arrayList = new ArrayList<>();
        List<Integer> linkedList = new LinkedList<>();
        
        // 填充100万数据
        for (int i = 0; i < 1000000; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
        
        // 测试ArrayList随机访问
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            arrayList.get(500000);  // 访问中间元素
        }
        System.out.println("ArrayList耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        // 测试LinkedList随机访问
        start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            linkedList.get(500000);  // 性能灾难!
        }
        System.out.println("LinkedList耗时: " + (System.currentTimeMillis() - start) + "ms");
    }
}

插入删除性能:

// 技术栈:Java 11
public class InsertDeleteTest {
    public static void main(String[] args) {
        List<Integer> arrayList = new ArrayList<>();
        List<Integer> linkedList = new LinkedList<>();
        
        // 初始化数据
        for (int i = 0; i < 50000; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
        
        // ArrayList头部插入
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            arrayList.add(0, i);  // 每次都要移动后面所有元素
        }
        System.out.println("ArrayList头部插入耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        // LinkedList头部插入
        start = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            linkedList.addFirst(i);  // 只需修改指针
        }
        System.out.println("LinkedList头部插入耗时: " + (System.currentTimeMillis() - start) + "ms");
    }
}

实际开发中,ArrayList更适合:

  • 需要频繁随机访问元素
  • 元素数量相对稳定,主要在末尾增删
  • 内存空间比较紧张的情况

LinkedList更适合:

  • 需要频繁在任意位置插入删除
  • 不需要频繁随机访问
  • 可以实现队列/双端队列等特殊结构

三、HashMap与TreeMap全面解析

HashMap就像是个魔法口袋,你扔进去的东西会通过哈希算法自动分类存放。TreeMap则像个自动整理的书架,总是按照字母顺序排列。

基础使用示例:

// 技术栈:Java 11
import java.util.*;

public class MapDemo {
    public static void main(String[] args) {
        // HashMap示例
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("苹果", 10);
        hashMap.put("香蕉", 5);
        hashMap.put("橙子", 8);
        System.out.println("HashMap内容(无序): " + hashMap);
        
        // TreeMap示例
        Map<String, Integer> treeMap = new TreeMap<>();
        treeMap.put("苹果", 10);
        treeMap.put("香蕉", 5);
        treeMap.put("橙子", 8);
        System.out.println("TreeMap内容(有序): " + treeMap);
    }
}

性能对比测试:

// 技术栈:Java 11
public class MapPerformanceTest {
    public static void main(String[] args) {
        Map<Integer, String> hashMap = new HashMap<>();
        Map<Integer, String> treeMap = new TreeMap<>();
        int elementCount = 100000;
        
        // 插入性能测试
        long start = System.currentTimeMillis();
        for (int i = 0; i < elementCount; i++) {
            hashMap.put(i, "Value_" + i);
        }
        System.out.println("HashMap插入耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        start = System.currentTimeMillis();
        for (int i = 0; i < elementCount; i++) {
            treeMap.put(i, "Value_" + i);
        }
        System.out.println("TreeMap插入耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        // 查询性能测试
        start = System.currentTimeMillis();
        for (int i = 0; i < elementCount; i++) {
            hashMap.get(i);
        }
        System.out.println("HashMap查询耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        start = System.currentTimeMillis();
        for (int i = 0; i < elementCount; i++) {
            treeMap.get(i);
        }
        System.out.println("TreeMap查询耗时: " + (System.currentTimeMillis() - start) + "ms");
    }
}

HashMap的底层是数组+链表/红黑树的结构。当链表长度超过8时,会自动转为红黑树。而TreeMap则完全基于红黑树实现,保证了元素的有序性。

实际应用场景:

  • HashMap适合:
    • 需要快速查找的场景
    • 不关心元素顺序
    • 需要存储大量数据
  • TreeMap适合:
    • 需要保持元素有序
    • 需要范围查找(如查找A-C开头的所有键)
    • 愿意牺牲部分性能换取有序性

四、综合应用与最佳实践

在实际项目中,我们经常需要根据业务特点选择合适的集合。比如开发一个学生管理系统:

// 技术栈:Java 11
import java.util.*;

class Student {
    String id;
    String name;
    int score;
    
    public Student(String id, String name, int score) {
        this.id = id;
        this.name = name;
        this.score = score;
    }
    
    @Override
    public String toString() {
        return name + "(" + score + "分)";
    }
}

public class SchoolSystem {
    public static void main(String[] args) {
        // 快速查找学生用HashMap
        Map<String, Student> studentMap = new HashMap<>();
        studentMap.put("S1001", new Student("S1001", "张三", 85));
        studentMap.put("S1002", new Student("S1002", "李四", 92));
        
        // 需要按分数排序用TreeMap
        Map<Integer, List<Student>> scoreMap = new TreeMap<>(Comparator.reverseOrder());
        addStudentToScoreMap(scoreMap, studentMap.get("S1001"));
        addStudentToScoreMap(scoreMap, studentMap.get("S1002"));
        
        System.out.println("按分数排序:");
        scoreMap.forEach((score, students) -> 
            System.out.println(score + "分: " + students));
    }
    
    private static void addStudentToScoreMap(Map<Integer, List<Student>> map, Student student) {
        map.computeIfAbsent(student.score, k -> new ArrayList<>()).add(student);
    }
}

常见坑点与解决方案:

  1. HashMap的线程不安全:
// 解决方案1:使用Collections.synchronizedMap
Map<String, String> safeMap = Collections.synchronizedMap(new HashMap<>());

// 解决方案2:使用ConcurrentHashMap
Map<String, String> concurrentMap = new ConcurrentHashMap<>();
  1. 对象作为HashMap键的注意点:
class Person {
    String id;
    
    // 必须重写hashCode和equals方法
    @Override
    public int hashCode() {
        return id.hashCode();
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Person)) return false;
        return id.equals(((Person)obj).id);
    }
}
  1. 初始容量设置:
// 预估有1000个元素,负载因子0.75
Map<String, String> optimizedMap = new HashMap<>(1333); // 1000/0.75
List<String> optimizedList = new ArrayList<>(1000);

五、总结与选择指南

经过上面的详细对比,我们可以得出以下结论:

  1. ArrayList vs LinkedList:

    • 选择ArrayList当:查询多,增删少,内存有限
    • 选择LinkedList当:增删多,查询少,需要实现特殊数据结构
  2. HashMap vs TreeMap:

    • 选择HashMap当:需要极速查找,不关心顺序
    • 选择TreeMap当:需要有序遍历,愿意接受稍慢的访问速度
  3. 综合建议:

    • 大部分情况下ArrayList+HashMap的组合就能满足需求
    • 在特别关注性能的场景下,才需要考虑LinkedList和TreeMap
    • Java8后的HashMap性能大幅优化,几乎可以应对90%的场景

最后记住,没有最好的集合,只有最适合的集合。理解业务需求,了解数据结构特点,才能做出最合理的选择。