一、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);
}
}
常见坑点与解决方案:
- HashMap的线程不安全:
// 解决方案1:使用Collections.synchronizedMap
Map<String, String> safeMap = Collections.synchronizedMap(new HashMap<>());
// 解决方案2:使用ConcurrentHashMap
Map<String, String> concurrentMap = new ConcurrentHashMap<>();
- 对象作为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);
}
}
- 初始容量设置:
// 预估有1000个元素,负载因子0.75
Map<String, String> optimizedMap = new HashMap<>(1333); // 1000/0.75
List<String> optimizedList = new ArrayList<>(1000);
五、总结与选择指南
经过上面的详细对比,我们可以得出以下结论:
ArrayList vs LinkedList:
- 选择ArrayList当:查询多,增删少,内存有限
- 选择LinkedList当:增删多,查询少,需要实现特殊数据结构
HashMap vs TreeMap:
- 选择HashMap当:需要极速查找,不关心顺序
- 选择TreeMap当:需要有序遍历,愿意接受稍慢的访问速度
综合建议:
- 大部分情况下ArrayList+HashMap的组合就能满足需求
- 在特别关注性能的场景下,才需要考虑LinkedList和TreeMap
- Java8后的HashMap性能大幅优化,几乎可以应对90%的场景
最后记住,没有最好的集合,只有最适合的集合。理解业务需求,了解数据结构特点,才能做出最合理的选择。
评论