选用HashSet实现抽奖黑名单校验,因其O(1)平均查找时间复杂度可快速判断用户是否在黑名单中;相比ArrayList遍历(O(n))和HashMap冗余value,HashSet更轻量、语义更清晰。

用 HashSet 实现抽奖黑名单校验,核心是利用其 O(1) 平均时间复杂度的查找性能,快速判断用户是否在黑名单中,避免中奖。
为什么选 HashSet 而不是 ArrayList 或 HashMap
HashSet 底层基于 HashMap 实现,只存 key,无序、不允许重复,且不含 null(除非显式添加 null)。相比 ArrayList 的遍历查找(O(n)),它查一个 ID 几乎是“一锤定音”;相比 HashMap,它更轻量——不需要维护 value,语义也更清晰:我们只关心“这个用户在不在黑名单里”,不关心“他在黑名单里的备注是什么”。
黑名单数据初始化方式
实际项目中黑名单可能来自配置文件、数据库或 Redis。为保证高性能,建议启动时一次性加载进内存 HashSet,并支持热更新(如监听配置变更)。
- 从配置文件读取(如 application.yml 中的 black-list: [1001, 1002, 1005])→ 转为 Set
- 从数据库查询:SELECT user_id FROM t_blacklist WHERE status = 1 → 结果 set.add(id)
- 注意类型统一:用户 ID 用 Long 或 String 都可以,但务必全程一致;推荐 Long(数值型 ID 更常见,且比 String 占用更少内存、哈希更稳定)
抽奖前的黑名单拦截逻辑
在生成中奖结果前,先检查当前参与用户 ID 是否存在于黑名单 Set 中。是则跳过,不计入抽奖池;也可直接返回“您暂无抽奖资格”。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
立即学习“Java免费学习笔记(深入)”;
private static final Set<Long> BLACKLIST = new HashSet<>();
// 初始化示例(实际应放于初始化方法或 Bean 中)
static {
BLACKLIST.add(1001L);
BLACKLIST.add(1005L);
BLACKLIST.add(2023L);
}
public boolean canParticipate(long userId) {
return !BLACKLIST.contains(userId); // true 表示可参与
}
// 抽奖主流程中调用
if (canParticipate(userId)) {
addToLotteryPool(userId);
}
线程安全与动态更新注意事项
如果黑名单会运行时更新(比如运营后台实时拉黑),需考虑并发安全:
- 读多写少场景:用 Collections.synchronizedSet(new HashSet()),或更推荐 CopyOnWriteArraySet(但注意它适合读极多、写极少;频繁写会导致复制开销大)
- 高并发 + 频繁更新:改用 ConcurrentHashMap.newKeySet()(JDK 8+),它线程安全、性能好、API 与 HashSet 几乎一致
- 更新操作要原子:add/remove 建议封装成 service 方法,避免多处直接操作集合
不复杂但容易忽略:别忘了单元测试覆盖“黑名单内用户被拦截”和“白名单用户正常进入抽奖池”两种情况,再加个空集合、null 入参的边界测试就更稳了。

















