Java 常用类实战:数组 / 集合操作与随机数生成全解析
理论学得再好,不如动手练一遍。前面我们学了包装类、工具类(Arrays/Collections/Objects)和数学类(Math),今天通过三个实战案例,带你在实际场景中灵活运用这些知识:数组排序与查找、集合排序与反转、随机数生成。每个案例都包含需求分析、代码实现和避坑指南,配合图解让你吃透用法!
一、实战案例 1:数组排序与查找 —— 学生成绩管理
需求:
对一个班级的学生成绩(int 数组)进行排序,然后查找指定分数的学生是否存在,若存在则返回其索引(支持重复分数)。
分析:
- 排序:用
Arrays.sort()对 int 数组排序; - 查找:先用
Arrays.binarySearch()找目标分数,再处理重复分数的索引(因为二分查找返回的是任意匹配项); - 包装类:可能涉及 int 与 Integer 的自动装箱(如将数组转为 List 时)。
代码实现:
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
public class ScoreManager {
public static void main(String[] args) {
// 1. 定义学生成绩数组
int[] scores = {85, 92, 78, 92, 65, 85, 100};
System.out.println("原始成绩:" + Arrays.toString(scores)); // [85, 92, 78, 92, 65, 85, 100]
// 2. 排序(升序)
Arrays.sort(scores);
System.out.println("排序后:" + Arrays.toString(scores)); // [65, 78, 85, 85, 92, 92, 100]
// 3. 查找目标分数(如92)
int target = 92;
int firstIndex = Arrays.binarySearch(scores, target);
// 处理查找结果:binarySearch返回任意匹配项,需找出所有索引
if (firstIndex < 0) {
System.out.println("未找到分数:" + target);
} else {
// 收集所有相同分数的索引
List<Integer> allIndexes = new ArrayList<>();
// 向左找所有相同值
int temp = firstIndex;
while (temp >= 0 && scores[temp] == target) {
allIndexes.add(temp);
temp--;
}
// 向右找所有相同值(排除已添加的firstIndex)
temp = firstIndex + 1;
while (temp < scores.length && scores[temp] == target) {
allIndexes.add(temp);
temp++;
}
System.out.println("分数" + target + "的所有索引:" + allIndexes); // [4, 5]
}
}
}
关键点解析:
-
Arrays.sort()的底层:对基本类型(如 int)用双轴快速排序(比传统快排更高效),对对象数组用TimSort(归并排序的优化版)。排序后数组会被修改(原地排序)。 -
binarySearch()的坑:- 必须在已排序的数组上使用,否则结果不可靠;
- 找到时返回任意匹配项的索引(非第一个),需手动遍历找所有重复值;
- 未找到时返回
-(插入点 + 1)(插入点是目标值应插入的位置)。
-
数组转集合的注意:若用
Arrays.asList(scores),由于scores是 int [](基本类型数组),会得到List<int[]>而非List<Integer>。正确做法:先将 int 转为 Integer 数组,再转集合:Integer[] scoreObjects = new Integer[scores.length]; for (int i = 0; i < scores.length; i++) { scoreObjects[i] = scores[i]; // 自动装箱 } List<Integer> scoreList = Arrays.asList(scoreObjects); // 正确的List<Integer>
数组排序与查找流程图解:

二、实战案例 2:集合排序与反转 —— 商品列表管理
需求:
对一个商品列表(List<Goods>)进行排序:先按价格升序,若价格相同则按销量降序;再将排序后的列表反转,并统计指定价格区间的商品数量。
分析:
- 排序:用
Collections.sort(),配合Comparator实现自定义排序规则; - 反转:用
Collections.reverse()翻转列表顺序; - 统计:用
Collections.frequency()或手动遍历(更灵活)。
代码实现:
import java.util.*;
// 商品类
class Goods {
private String name;
private double price;
private int sales; // 销量
public Goods(String name, double price, int sales) {
this.name = name;
this.price = price;
this.sales = sales;
}
// getter方法(排序需要)
public double getPrice() { return price; }
public int getSales() { return sales; }
@Override
public String toString() {
return name + "(价格:" + price + ", 销量:" + sales + ")";
}
}
public class GoodsManager {
public static void main(String[] args) {
// 1. 创建商品列表
List<Goods> goodsList = new ArrayList<>();
goodsList.add(new Goods("手机", 3999, 1000));
goodsList.add(new Goods("平板", 2999, 500));
goodsList.add(new Goods("耳机", 799, 2000));
goodsList.add(new Goods("手表", 2999, 800)); // 与平板同价,销量更高
System.out.println("原始列表:");
goodsList.forEach(System.out::println);
// 2. 自定义排序:先按价格升序,同价按销量降序
Collections.sort(goodsList, new Comparator<Goods>() {
@Override
public int compare(Goods g1, Goods g2) {
// 先比较价格(升序)
if (g1.getPrice() != g2.getPrice()) {
return Double.compare(g1.getPrice(), g2.getPrice());
}
// 价格相同,比较销量(降序)
return Integer.compare(g2.getSales(), g1.getSales());
}
});
System.out.println("\n排序后(价格升序,同价销量降序):");
goodsList.forEach(System.out::println); // 耳机 → 手表 → 平板 → 手机
// 3. 反转列表
Collections.reverse(goodsList);
System.out.println("\n反转后:");
goodsList.forEach(System.out::println); // 手机 → 平板 → 手表 → 耳机
// 4. 统计价格在2000-4000的商品数量
long count = goodsList.stream()
.filter(g -> g.getPrice() >= 2000 && g.getPrice() <= 4000)
.count();
System.out.println("\n价格在2000-4000的商品数量:" + count); // 3(手机、平板、手表)
}
}
关键点解析:
-
Comparator与Comparable的区别:Comparable:类自身实现(如Goods implements Comparable<Goods>),定义 “自然排序”;Comparator:外部定义排序规则,更灵活(如案例中按 “价格 + 销量” 排序)。推荐用Comparator,避免修改原始类。
-
Collections.reverse()的底层:对 List 进行原地反转(修改原集合),通过交换首尾元素实现(时间复杂度 O (n/2))。 -
不可变集合的坑:若列表是
Collections.unmodifiableList()创建的不可变集合,调用sort()或reverse()会抛UnsupportedOperationException。
集合排序与反转图解:

三、实战案例 3:随机数生成 —— 验证码与抽奖系统
需求:
实现两个功能:1)生成 6 位数字验证码;2)从 10 个用户中随机抽取 3 个中奖者(不重复)。
分析:
- 验证码:用
Math.random()生成[0, 1000000)的随机数,不足 6 位补 0; - 抽奖:用
ThreadLocalRandom(Java 7+,线程安全且高效)生成随机索引,配合Collections.shuffle()打乱列表后取前 3 个。
代码实现:
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
public class RandomDemo {
public static void main(String[] args) {
// 1. 生成6位数字验证码
String verificationCode = generateCode();
System.out.println("6位验证码:" + verificationCode); // 如:085219
// 2. 随机抽奖(10个用户抽3个)
List<String> users = Arrays.asList(
"用户1", "用户2", "用户3", "用户4", "用户5",
"用户6", "用户7", "用户8", "用户9", "用户10"
);
List<String> winners = drawWinners(users, 3);
System.out.println("中奖用户:" + winners); // 如:[用户3, 用户7, 用户1]
}
// 生成6位数字验证码
private static String generateCode() {
// Math.random()生成[0.0,1.0) → 乘1000000得[0.0,1000000.0) → 转int得0-999999
int code = (int) (Math.random() * 1000000);
// 不足6位补0(格式化输出)
return String.format("%06d", code);
}
// 从用户列表中随机抽取n个不重复中奖者
private static List<String> drawWinners(List<String> users, int n) {
if (n > users.size()) {
throw new IllegalArgumentException("抽奖人数不能超过总人数");
}
// 复制列表(避免修改原列表)
List<String> copy = new ArrayList<>(users);
// 打乱顺序(用ThreadLocalRandom的实例,比Collections.shuffle()默认随机数更高效)
ThreadLocalRandom random = ThreadLocalRandom.current();
Collections.shuffle(copy, random);
// 取前n个
return copy.subList(0, n);
}
}
关键点解析:
-
Math.random()vsThreadLocalRandom:Math.random():底层用一个静态Random实例,多线程并发时可能因锁竞争效率低;ThreadLocalRandom:线程私有,无锁竞争,性能更好(推荐多线程场景使用)。
-
验证码补 0 技巧:用
String.format("%06d", code),%06d表示 “至少 6 位,不足补 0”(如123→000123)。 -
抽奖去重方案:
- 方案 1:
Collections.shuffle()打乱列表后取前 n 个(简单高效); - 方案 2:生成随机索引,用
Set去重(适合不允许修改列表的场景)。
- 方案 1:
随机数生成图解:

四、实战总结:核心类与方法速查表
| 场景 | 核心类 | 关键方法 | 注意事项 |
|---|---|---|---|
| 数组排序 / 查找 | Arrays |
sort()、binarySearch()、toString() |
二分查找需先排序;asList()基本类型坑 |
| 集合排序 / 反转 | Collections |
sort()、reverse()、shuffle() |
自定义排序用Comparator;不可变集合不能修改 |
| 随机数生成 | Math/ThreadLocalRandom |
random()、nextInt() |
多线程用ThreadLocalRandom;控制范围避免溢出 |
| 对象比较 / 空值处理 | Objects |
equals()、isNull()、requireNonNull() |
避免空指针;deepEquals()用于嵌套结构 |
通过这三个案例,你会发现:Java 的工具类不是孤立的,实际开发中往往需要结合使用(如Arrays转集合后用Collections排序,Math生成随机数后用Arrays填充数组)。关键是理解每个类的设计初衷,记住高频方法的用法和坑点,遇到问题时能第一时间想到用哪个工具类解决。
最后建议:把这些案例代码敲一遍,尝试修改需求(如给商品排序增加名称维度、抽奖增加权重),在实践中加深理解!
版权声明:本博客内容为原创,转载请保留原文链接及作者信息。
更多推荐
所有评论(0)