集合框架闪亮登场!它就像是一个功能强大的"数据管理工具箱",让你轻松处理各种数据集合
1.集合框架全景图
text
Collection(接口) Map(接口)
├── List(有序可重复) ├── HashMap(最常用)
│ ├── ArrayList(数组实现) ├── TreeMap(有序)
│ ├── LinkedList(链表实现) └── LinkedHashMap(保持插入顺序)
│ └── Vector(线程安全)
├── Set(无序不重复)
│ ├── HashSet(最常用)
│ ├── TreeSet(有序)
│ └── LinkedHashSet(保持插入顺序)
└── Queue(队列)
├── PriorityQueue(优先级队列)
└── LinkedList(也可作队列)
本篇主要重点学习以下三点
- ArrayList - 像可扩展的数组
- HashMap - 像字典或电话本
- HashSet - 像数学中的集合
2.ArrayList:万能的可变数组
2.1 ArrayList基础操作
Java
import java.util.ArrayList;
import java.util.Collections;
public class ArrayListDemo {
public static void main(String[] args) {
System.out.println("=== ArrayList基础操作 ===");
// 1. 创建ArrayList(不用指定大小!)
ArrayList<String> fruits = new ArrayList<>();
// 2. 添加元素(自动扩容!)
fruits.add("苹果");
fruits.add("香蕉");
fruits.add("橙子");
fruits.add("葡萄");
fruits.add("西瓜");
System.out.println("添加后的水果列表: " + fruits);
// 3. 在指定位置插入元素
fruits.add(2, "芒果"); // 在索引2处插入
System.out.println("插入芒果后: " + fruits);
// 4. 获取元素
String firstFruit = fruits.get(0);
System.out.println("第一个水果: " + firstFruit);
// 5. 修改元素
fruits.set(1, "草莓"); // 把索引1的香蕉改为草莓
System.out.println("修改后: " + fruits);
// 6. 删除元素
fruits.remove(3); // 删除索引3的元素
System.out.println("删除索引3后: " + fruits);
fruits.remove("西瓜"); // 删除指定元素
System.out.println("删除西瓜后: " + fruits);
// 7. 获取大小
System.out.println("现在有多少种水果?" + fruits.size() + "种");
// 8. 检查是否包含某元素
boolean hasApple = fruits.contains("苹果");
System.out.println("有苹果吗?" + hasApple);
// 9. 查找元素位置
int index = fruits.indexOf("芒果");
System.out.println("芒果在索引" + index + "位置");
// 10. 清空列表
fruits.clear();
System.out.println("清空后: " + fruits);
System.out.println("现在是空的吗?" + fruits.isEmpty());
}
}
运行结果:
Java
=== ArrayList基础操作 ===
添加后的水果列表: [苹果, 香蕉, 橙子, 葡萄, 西瓜]
插入芒果后: [苹果, 香蕉, 芒果, 橙子, 葡萄, 西瓜]
修改后: [苹果, 草莓, 芒果, 橙子, 葡萄, 西瓜]
删除索引3后: [苹果, 草莓, 芒果, 葡萄, 西瓜]
删除西瓜后: [苹果, 草莓, 芒果, 葡萄]
现在有多少种水果?4种
有苹果吗?true
芒果在索引2位置
清空后: []
现在是空的吗?true
2.2 ArrayList遍历和排序
Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
public class ArrayListTraversal {
public static void main(String[] args) {
System.out.println("=== ArrayList遍历和排序 ===");
// 创建一个学生名单
ArrayList<String> students = new ArrayList<>();
students.add("王五");
students.add("张三");
students.add("李四");
students.add("赵六");
students.add("钱七");
System.out.println("原始名单: " + students);
// 1. 使用for循环遍历(知道索引时)
System.out.println("\n1. 使用for循环遍历:");
for (int i = 0; i < students.size(); i++) {
System.out.println("学生" + (i + 1) + ": " + students.get(i));
}
// 2. 使用增强for循环(最常用!)
System.out.println("\n2. 使用增强for循环:");
for (String student : students) {
System.out.println("学生: " + student);
}
// 3. 使用Iterator(迭代器)
System.out.println("\n3. 使用Iterator遍历:");
Iterator<String> iterator = students.iterator();
while (iterator.hasNext()) {
System.out.println("学生: " + iterator.next());
}
// 4. 排序(按字母顺序)
System.out.println("\n4. 排序后的名单:");
Collections.sort(students);
System.out.println(students);
// 5. 打乱顺序
System.out.println("\n5. 随机打乱顺序:");
Collections.shuffle(students);
System.out.println(students);
// 6. 反转顺序
System.out.println("\n6. 反转顺序:");
Collections.reverse(students);
System.out.println(students);
// 7. 学生成绩排序示例
System.out.println("\n=== 学生成绩排序 ===");
ArrayList<Integer> scores = new ArrayList<>();
scores.add(85);
scores.add(92);
scores.add(78);
scores.add(95);
scores.add(88);
System.out.println("原始成绩: " + scores);
Collections.sort(scores); // 升序排序
System.out.println("升序排序: " + scores);
Collections.sort(scores, Collections.reverseOrder()); // 降序排序
System.out.println("降序排序: " + scores);
// 8. 查找最大值和最小值
int maxScore = Collections.max(scores);
int minScore = Collections.min(scores);
System.out.println("最高分: " + maxScore);
System.out.println("最低分: " + minScore);
}
}
2.3 综合示例:学生管理系统
Java
import java.util.ArrayList;
import java.util.Scanner;
public class StudentManagementSystem {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
ArrayList<Student> students = new ArrayList<>();
System.out.println("=== 学生管理系统 ===");
boolean running = true;
while (running) {
System.out.println("\n请选择操作:");
System.out.println("1. 添加学生");
System.out.println("2. 显示所有学生");
System.out.println("3. 查找学生");
System.out.println("4. 删除学生");
System.out.println("5. 更新学生信息");
System.out.println("6. 按成绩排序");
System.out.println("7. 退出");
System.out.print("请输入选择 (1-7): ");
int choice = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
switch (choice) {
case 1:
addStudent(scanner, students);
break;
case 2:
showAllStudents(students);
break;
case 3:
findStudent(scanner, students);
break;
case 4:
deleteStudent(scanner, students);
break;
case 5:
updateStudent(scanner, students);
break;
case 6:
sortStudents(students);
break;
case 7:
running = false;
System.out.println("感谢使用,再见!");
break;
default:
System.out.println("无效选择,请重新输入!");
}
}
scanner.close();
}
// 添加学生
private static void addStudent(Scanner scanner, ArrayList<Student> students) {
System.out.println("\n=== 添加学生 ===");
System.out.print("请输入学号: ");
String id = scanner.nextLine();
// 检查学号是否已存在
for (Student s : students) {
if (s.getId().equals(id)) {
System.out.println("学号已存在,添加失败!");
return;
}
}
System.out.print("请输入姓名: ");
String name = scanner.nextLine();
System.out.print("请输入年龄: ");
int age = scanner.nextInt();
System.out.print("请输入成绩: ");
double score = scanner.nextDouble();
scanner.nextLine(); // 消耗换行符
Student student = new Student(id, name, age, score);
students.add(student);
System.out.println("学生添加成功!");
}
// 显示所有学生
private static void showAllStudents(ArrayList<Student> students) {
System.out.println("\n=== 所有学生信息 ===");
if (students.isEmpty()) {
System.out.println("暂无学生信息!");
return;
}
System.out.printf("%-10s %-10s %-5s %-10s\n",
"学号", "姓名", "年龄", "成绩");
System.out.println("-".repeat(35));
for (Student student : students) {
System.out.printf("%-10s %-10s %-5d %-10.2f\n",
student.getId(),
student.getName(),
student.getAge(),
student.getScore());
}
}
// 查找学生
private static void findStudent(Scanner scanner, ArrayList<Student> students) {
System.out.println("\n=== 查找学生 ===");
System.out.print("请输入要查找的学号或姓名: ");
String keyword = scanner.nextLine();
boolean found = false;
for (Student student : students) {
if (student.getId().equals(keyword) ||
student.getName().contains(keyword)) {
System.out.println("找到学生:");
System.out.println(student);
found = true;
}
}
if (!found) {
System.out.println("未找到匹配的学生!");
}
}
// 删除学生
private static void deleteStudent(Scanner scanner, ArrayList<Student> students) {
System.out.println("\n=== 删除学生 ===");
System.out.print("请输入要删除的学号: ");
String id = scanner.nextLine();
boolean removed = false;
for (int i = 0; i < students.size(); i++) {
if (students.get(i).getId().equals(id)) {
students.remove(i);
removed = true;
break;
}
}
if (removed) {
System.out.println("学生删除成功!");
} else {
System.out.println("未找到该学号的学生!");
}
}
// 更新学生信息
private static void updateStudent(Scanner scanner, ArrayList<Student> students) {
System.out.println("\n=== 更新学生信息 ===");
System.out.print("请输入要更新的学号: ");
String id = scanner.nextLine();
for (Student student : students) {
if (student.getId().equals(id)) {
System.out.println("找到学生:" + student);
System.out.print("请输入新姓名(直接回车保持原值): ");
String newName = scanner.nextLine();
if (!newName.isEmpty()) {
student.setName(newName);
}
System.out.print("请输入新年龄(输入0保持原值): ");
int newAge = scanner.nextInt();
if (newAge > 0) {
student.setAge(newAge);
}
System.out.print("请输入新成绩(输入负数保持原值): ");
double newScore = scanner.nextDouble();
scanner.nextLine(); // 消耗换行符
if (newScore >= 0) {
student.setScore(newScore);
}
System.out.println("学生信息更新成功!");
return;
}
}
System.out.println("未找到该学号的学生!");
}
// 按成绩排序
private static void sortStudents(ArrayList<Student> students) {
if (students.isEmpty()) {
System.out.println("暂无学生信息!");
return;
}
// 使用冒泡排序(理解原理)
for (int i = 0; i < students.size() - 1; i++) {
for (int j = 0; j < students.size() - 1 - i; j++) {
if (students.get(j).getScore() < students.get(j + 1).getScore()) {
// 交换位置
Student temp = students.get(j);
students.set(j, students.get(j + 1));
students.set(j + 1, temp);
}
}
}
System.out.println("按成绩降序排序完成!");
showAllStudents(students);
}
}
// 学生类
class Student {
private String id;
private String name;
private int age;
private double score;
public Student(String id, String name, int age, double score) {
this.id = id;
this.name = name;
this.age = age;
this.score = score;
}
// Getter和Setter方法
public String getId() { return id; }
public String getName() { return name; }
public int getAge() { return age; }
public double getScore() { return score; }
public void setName(String name) { this.name = name; }
public void setAge(int age) { this.age = age; }
public void setScore(double score) { this.score = score; }
@Override
public String toString() {
return String.format("学号: %s, 姓名: %s, 年龄: %d, 成绩: %.2f",
id, name, age, score);
}
}
3.HashMap:键值对的"字典"
3.1 HashMap基础操作
Java
import java.util.HashMap;
import java.util.Map;
public class HashMapDemo {
public static void main(String[] args) {
System.out.println("=== HashMap基础操作 ===");
// 1. 创建HashMap(键-值对存储)
HashMap<String, String> phoneBook = new HashMap<>();
// 2. 添加键值对
phoneBook.put("张三", "13800138000");
phoneBook.put("李四", "13900139000");
phoneBook.put("王五", "13700137000");
phoneBook.put("赵六", "13600136000");
System.out.println("电话簿: " + phoneBook);
// 3. 获取值
String phoneNumber = phoneBook.get("张三");
System.out.println("张三的电话: " + phoneNumber);
// 4. 如果键不存在,返回默认值
String unknown = phoneBook.getOrDefault("钱七", "未知号码");
System.out.println("钱七的电话: " + unknown);
// 5. 检查是否包含键或值
boolean hasZhangSan = phoneBook.containsKey("张三");
boolean hasNumber = phoneBook.containsValue("13900139000");
System.out.println("有张三吗?" + hasZhangSan);
System.out.println("有13900139000这个号码吗?" + hasNumber);
// 6. 删除键值对
phoneBook.remove("王五");
System.out.println("删除王五后: " + phoneBook);
// 7. 修改值
phoneBook.put("李四", "13999999999"); // 覆盖原来的值
System.out.println("修改李四号码后: " + phoneBook);
// 8. 获取大小
System.out.println("电话簿中有 " + phoneBook.size() + " 个联系人");
// 9. 获取所有键
System.out.println("所有联系人: " + phoneBook.keySet());
// 10. 获取所有值
System.out.println("所有号码: " + phoneBook.values());
// 11. 清空
phoneBook.clear();
System.out.println("清空后: " + phoneBook);
System.out.println("现在是空的吗?" + phoneBook.isEmpty());
}
}
3.2 HashMap遍历
Java
import java.util.HashMap;
import java.util.Map;
public class HashMapTraversal {
public static void main(String[] args) {
System.out.println("=== HashMap遍历 ===");
// 创建学生成绩表
HashMap<String, Integer> scores = new HashMap<>();
scores.put("张三", 85);
scores.put("李四", 92);
scores.put("王五", 78);
scores.put("赵六", 95);
scores.put("钱七", 88);
System.out.println("学生成绩表: " + scores);
// 1. 遍历所有键
System.out.println("\n1. 遍历所有键:");
for (String name : scores.keySet()) {
System.out.println("学生: " + name);
}
// 2. 遍历所有值
System.out.println("\n2. 遍历所有值:");
for (int score : scores.values()) {
System.out.println("成绩: " + score);
}
// 3. 遍历所有键值对(最常用!)
System.out.println("\n3. 遍历所有键值对:");
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
String name = entry.getKey();
int score = entry.getValue();
System.out.println(name + "的成绩是: " + score);
}
// 4. 使用forEach方法(Java 8+)
System.out.println("\n4. 使用forEach方法:");
scores.forEach((name, score) -> {
System.out.println(name + " -> " + score);
});
// 5. 统计成绩
System.out.println("\n5. 成绩统计:");
int sum = 0;
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
String topStudent = "";
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
int score = entry.getValue();
sum += score;
if (score > max) {
max = score;
topStudent = entry.getKey();
}
if (score < min) {
min = score;
}
}
double average = (double) sum / scores.size();
System.out.println("平均分: " + average);
System.out.println("最高分: " + max + " (学生: " + topStudent + ")");
System.out.println("最低分: " + min);
}
}
3.3 综合示例:单词计数器
Java
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class WordCounter {
public static void main(String[] args) {
System.out.println("=== 单词计数器 ===");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一段英文(输入'END'结束):");
HashMap<String, Integer> wordCount = new HashMap<>();
while (true) {
String line = scanner.nextLine();
if (line.equals("END")) {
break;
}
// 分割单词(简单处理,按空格分割)
String[] words = line.split("\\s+");
for (String word : words) {
// 清理单词(去掉标点,转小写)
word = word.toLowerCase()
.replaceAll("[^a-z]", ""); // 去掉非字母字符
if (!word.isEmpty()) {
// 更新计数
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
}
}
// 显示结果
System.out.println("\n=== 单词统计结果 ===");
System.out.println("共发现 " + wordCount.size() + " 个不同的单词");
// 按出现次数排序(简单的实现)
System.out.println("\n单词出现次数(从高到低):");
// 转换为列表进行排序
List<Map.Entry<String, Integer>> entries = new ArrayList<>(wordCount.entrySet());
// 排序(按值降序)
entries.sort((a, b) -> b.getValue() - a.getValue());
// 显示前10个
int count = 0;
for (Map.Entry<String, Integer> entry : entries) {
System.out.printf("%-15s: %d次\n", entry.getKey(), entry.getValue());
count++;
if (count >= 10) break; // 只显示前10个
}
// 查找特定单词
System.out.print("\n请输入要查找的单词: ");
String searchWord = scanner.next().toLowerCase();
if (wordCount.containsKey(searchWord)) {
System.out.println(searchWord + " 出现了 " + wordCount.get(searchWord) + " 次");
} else {
System.out.println("未找到单词: " + searchWord);
}
scanner.close();
}
}
4.HashSet:不重复的"集合"
Java
import java.util.HashSet;
import java.util.TreeSet;
import java.util.LinkedHashSet;
public class HashSetDemo {
public static void main(String[] args) {
System.out.println("=== HashSet集合操作 ===");
// 1. HashSet(无序,不重复)
System.out.println("1. HashSet(无序不重复):");
HashSet<String> set1 = new HashSet<>();
set1.add("苹果");
set1.add("香蕉");
set1.add("橙子");
set1.add("苹果"); // 重复,不会被添加
set1.add("葡萄");
System.out.println("水果集合: " + set1);
System.out.println("有香蕉吗?" + set1.contains("香蕉"));
System.out.println("集合大小: " + set1.size());
// 2. 集合运算
System.out.println("\n2. 集合运算:");
HashSet<String> set2 = new HashSet<>();
set2.add("香蕉");
set2.add("葡萄");
set2.add("西瓜");
set2.add("芒果");
System.out.println("集合A: " + set1);
System.out.println("集合B: " + set2);
// 并集
HashSet<String> union = new HashSet<>(set1);
union.addAll(set2);
System.out.println("并集(A∪B): " + union);
// 交集
HashSet<String> intersection = new HashSet<>(set1);
intersection.retainAll(set2);
System.out.println("交集(A∩B): " + intersection);
// 差集
HashSet<String> difference = new HashSet<>(set1);
difference.removeAll(set2);
System.out.println("差集(A-B): " + difference);
// 3. TreeSet(有序)
System.out.println("\n3. TreeSet(自动排序):");
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);
numbers.add(3);
System.out.println("数字集合: " + numbers);
System.out.println("最小的数: " + numbers.first());
System.out.println("最大的数: " + numbers.last());
System.out.println("大于3的最小值: " + numbers.higher(3));
System.out.println("小于5的最大值: " + numbers.lower(5));
// 4. LinkedHashSet(保持插入顺序)
System.out.println("\n4. LinkedHashSet(保持插入顺序):");
LinkedHashSet<String> linkedSet = new LinkedHashSet<>();
linkedSet.add("第三");
linkedSet.add("第一");
linkedSet.add("第二");
linkedSet.add("第四");
System.out.println("插入顺序集合: " + linkedSet);
// 5. 实际应用:抽奖系统
System.out.println("\n5. 抽奖系统:");
HashSet<String> participants = new HashSet<>();
participants.add("张三");
participants.add("李四");
participants.add("王五");
participants.add("赵六");
participants.add("钱七");
participants.add("张三"); // 重复报名,自动去重
System.out.println("参与抽奖的名单: " + participants);
System.out.println("参与人数: " + participants.size());
// 转换为数组用于抽奖
String[] participantArray = participants.toArray(new String[0]);
// 模拟抽奖(随机选一个)
if (participantArray.length > 0) {
int luckyIndex = (int) (Math.random() * participantArray.length);
System.out.println("恭喜 " + participantArray[luckyIndex] + " 中奖!");
}
}
}
5.集合框架的注意事项
5.1 泛型的重要性
Java
import java.util.ArrayList;
public class GenericsDemo {
public static void main(String[] args) {
System.out.println("=== 泛型的重要性 ===");
// 没有泛型的ArrayList(不推荐!)
ArrayList list1 = new ArrayList(); // 警告:原始类型
list1.add("字符串");
list1.add(123); // 可以放任何类型
list1.add(45.67);
System.out.println("混合列表: " + list1);
// 问题:需要强制类型转换,容易出错
String str = (String) list1.get(0); // 可以
// String str2 = (String) list1.get(1); // 运行时错误!ClassCastException
// 使用泛型的ArrayList(推荐!)
ArrayList<String> list2 = new ArrayList<>();
list2.add("安全的字符串");
// list2.add(123); // 编译错误!类型安全
// list2.add(45.67); // 编译错误!
String safeStr = list2.get(0); // 不需要强制转换
System.out.println("类型安全的列表: " + list2);
}
}
5.2 集合的性能比较
Java
import java.util.*;
public class PerformanceComparison {
public static void main(String[] args) {
System.out.println("=== 集合性能比较 ===");
int elementCount = 100000;
// ArrayList vs LinkedList 插入性能
System.out.println("测试插入" + elementCount + "个元素的性能:");
// ArrayList
long start = System.currentTimeMillis();
ArrayList<Integer> arrayList = new ArrayList<>();
for (int i = 0; i < elementCount; i++) {
arrayList.add(i); // 在末尾添加,很快
}
long arrayListTime = System.currentTimeMillis() - start;
// LinkedList
start = System.currentTimeMillis();
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < elementCount; i++) {
linkedList.add(i); // 在末尾添加,也很快
}
long linkedListTime = System.currentTimeMillis() - start;
System.out.println("ArrayList插入时间: " + arrayListTime + "ms");
System.out.println("LinkedList插入时间: " + linkedListTime + "ms");
// 测试随机访问性能
System.out.println("\n测试随机访问性能:");
start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
int index = (int) (Math.random() * elementCount);
arrayList.get(index); // O(1) 很快
}
long arrayListAccessTime = System.currentTimeMillis() - start;
start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
int index = (int) (Math.random() * elementCount);
linkedList.get(index); // O(n) 很慢
}
long linkedListAccessTime = System.currentTimeMillis() - start;
System.out.println("ArrayList随机访问时间: " + arrayListAccessTime + "ms");
System.out.println("LinkedList随机访问时间: " + linkedListAccessTime + "ms");
// 总结
System.out.println("\n=== 选择指南 ===");
System.out.println("1. 需要频繁随机访问 → ArrayList");
System.out.println("2. 需要频繁在中间插入/删除 → LinkedList");
System.out.println("3. 需要快速查找键对应的值 → HashMap");
System.out.println("4. 需要不重复元素 → HashSet");
System.out.println("5. 需要自动排序 → TreeSet/TreeMap");
}
}
总结:集合框架学习要点
- ArrayList是万能的 - 大多数情况下都够用
- 可动态扩容
- 快速随机访问
- 简单易用
- HashMap是高效的 - 键值对查找利器
- 快速查找(O(1))
- 键不能重复
- 值可以重复
- HashSet去重复 - 需要不重复集合时用
- 自动去重
- 快速判断包含
- 集合运算方便
- 泛型是必须的 - 保证类型安全
- 避免类型转换错误
- 编译时检查类型
- 代码更清晰