Spigot插件开发:按概率从列表随机获取Rarity对象问题
问题分析与解决方案
原代码核心问题
- 概率被错误缩放:
random方法里将每个Rarity的概率除以4,完全扭曲了配置的概率值,导致实际生效的概率远低于预期。 - 随机数生成不规范:每次调用
random都新建Random实例,既浪费资源又可能导致随机结果不均匀。 - 概率总和超标:示例概率总和为
55+35+12+6+3+0.1=111.1%,超过100%,这会导致必然返回某个对象,不符合“允许不出结果”的需求。 - 无序遍历导致逻辑混乱:使用
HashMap.keySet()遍历,元素顺序不确定,调试和概率判断逻辑易出问题。
修正后的代码实现
private final Map<Integer, Rarity> classes = new HashMap<>(); // 复用同一个Random实例,避免重复创建 private final Random random = new Random(); public MobKillListener() { for (Map<Integer, Rarity> rarityMap : SchemePlugin.getInstance().getSchemeConfig().classes) { classes.putAll(rarityMap); } } @EventHandler public void onMobKill(EntityDeathEvent event) { if (!(event.getEntity().getKiller() instanceof Player)) return; // 将Rarity和对应的概率整理成有序列表 List<Map.Entry<Rarity, Double>> rarityList = new ArrayList<>(); double totalProbability = 0.0; for (Rarity rarity : classes.values()) { rarityList.add(new AbstractMap.SimpleEntry<>(rarity, rarity.rarity)); totalProbability += rarity.rarity; } // 若总概率超过100%,按比例缩放至100%(可选,也可改为提示配置错误) if (totalProbability > 100.0) { double scale = 100.0 / totalProbability; for (int i = 0; i < rarityList.size(); i++) { Map.Entry<Rarity, Double> entry = rarityList.get(i); rarityList.set(i, new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue() * scale)); } totalProbability = 100.0; } Rarity rarity = randomSelect(rarityList, totalProbability); if (rarity != null) { // 执行你的业务逻辑 } } // 正确的概率随机选择方法 private Rarity randomSelect(List<Map.Entry<Rarity, Double>> rarityList, double totalProbability) { // 生成0到100之间的随机数 double rand = random.nextDouble() * 100.0; double currentTotal = 0.0; for (Map.Entry<Rarity, Double> entry : rarityList) { double chance = entry.getValue(); currentTotal += chance; if (rand <= currentTotal) { System.out.printf("命中概率: %.2f%%, 当前累加值: %.2f, 随机数: %.2f%n", chance, currentTotal, rand); return entry.getKey(); } } // 随机数超过所有概率总和,返回null(无产出) System.out.printf("未命中任何对象,总概率: %.2f, 随机数: %.2f%n", totalProbability, rand); return null; }
关键优化点
- 概率逻辑修正:直接使用配置的原始概率值,不再做无意义的除法;若总概率超标,自动按比例缩放至100%,避免必然出结果的问题。
- 复用Random实例:使用类初始化的
random对象,保证随机结果的均匀性。 - 有序遍历:用
ArrayList存储Rarity和概率的键值对,遍历顺序稳定,便于调试和维护。 - 明确的空返回逻辑:当随机数大于所有Rarity的概率总和时,返回null,实现“允许不出结果”的需求。
额外说明
如果要严格保留配置的概率值(即使总和超过100%),同时保留“不出结果”的可能,可修改randomSelect方法的随机数生成逻辑:
// 随机数范围设为0到总概率和100的较大值,超过100的部分对应无产出 double maxRange = Math.max(100.0, totalProbability); double rand = random.nextDouble() * maxRange;
内容的提问来源于stack exchange,提问作者reiizl
相关产品推荐
相关产品推荐

