Java实现选择题选项乱序算法

文章目录

    • 前言
    • [一、 核心思路](#一、 核心思路)
    • 二、核心代码实现
      • [2.1 定义数据承载类 `Topic`](#2.1 定义数据承载类 Topic)
      • [2.2 乱序工具类 `TopicRandomUtil`](#2.2 乱序工具类 TopicRandomUtil)
    • [3. 实际业务场景模拟验证](#3. 实际业务场景模拟验证)
    • 总结

前言

在开发在线考试或问答系统时,经常会遇到一个痛点:考生容易通过背诵选项字母(例如"第一题选C,第二题选A")来作弊或应付考试。

为了解决这个问题,我们需要在系统渲染试卷时,将题目的选项顺序随机打乱。

这里的核心难点在于:选项顺序打乱后,系统必须依然能准确识别出哪一个是正确答案。

这就好比给房间里的物品换门牌号:物品本身没动,只是门上的字母标签换了,但我们需要时刻追踪那个存放"正确答案"的房间现在挂着什么字母。

一、 核心思路

原标签 (originalKey) 新标签 (newKey) 选项内容 (value) 是否为正确答案
A C JAVA2 EE
B A JAVA2 Card
C D JAVA2 ME
D B JAVA2 HE ✓ → 触发记录 keyNew = "B"

内容没有动,只是换了门牌号(字母标签)。正确答案的内容跟着走到了 B,所以正确的选项 Key 也同步更新为 B。

二、核心代码实现

为了实现这个功能,我们首先需要一个数据结构来承载选项和答案。

2.1 定义数据承载类 Topic

这个类非常简单,主要用于封装选项的 Map(选项字母 -> 选项内容)和正确的 key

Java 复制代码
import java.util.Map;

public class Topic {
    private Map<String, String> option;  // 选项映射,例如 {"A": "JAVA2 EE", ...}
    private String key;                  // 正确答案的选项字母

    public Topic() {}

    public Topic(Map<String, String> option, String key) {
        this.option = option;
        this.key = key;
    }

    public Map<String, String> getOption() { return option; }
    public void setOption(Map<String, String> option) { this.option = option; }
    public String getKey() { return key; }
    public void setKey(String key) { this.key = key; }
}

2.2 乱序工具类 TopicRandomUtil

这是整个乱序逻辑的引擎。利用 Collections.shuffle() 来实现"摇匀门牌号"的操作。

在 Java 中,HashMapkeySet() 是无序的,它既不保证插入顺序,也不保证字母顺序。它仅仅是根据 Key 的哈希值(Hash Code)计算出的数组下标来存放的。

Java 复制代码
import java.util.*;

public class TopicRandomUtil {

    /**
     * 乱序Map元素,记录对应答案key
     * @param option 原题目选项
     * @param key    原正确答案
     * @return Topic 乱序后的选项和新的正确答案
     */
    static public Topic random(Map<String, String> option, String key) {
        // 1. 获取原有的门牌号集合 (A, B, C, D)
        Set<String> keySet = option.keySet();
        ArrayList<String> keyList = new ArrayList<>(keySet);
        
        // 2. 将门牌号列表乱序 (例如变成了 C, A, D, B)
        Collections.shuffle(keyList);
        
        // 3. 准备一个新的Map来存放换好门牌号的选项
        HashMap<String, String> optionNew = new HashMap<>();
        
        int idx = 0;
        String answerNew = ""; // 用于记录新的正确答案门牌号
        
        // 4. 遍历原有的门牌号,依次贴上新的门牌号
        for (String originKey : keySet) {
            String newKey = keyList.get(idx++);
            
            // 5. 判断当前被替换门牌号的原选项,是不是正确答案
            if (key.equals(originKey)) {
                answerNew = newKey; // 如果是,记录它贴上的新门牌号
            }
            
            // 6. 将新门牌号和原内容存入新Map
            optionNew.put(newKey, option.get(originKey));
        }

        return new Topic(optionNew, answerNew);
    }
}

算法可视化流程:

3. 实际业务场景模拟验证

为了直观看到效果,我们编写一个简单的 main 方法来模拟这个过程:

Java 复制代码
import java.util.HashMap;
import java.util.Map;

public class ExamSimulation {

    public static void main(String[] args) {
        // 1. 模拟从数据库中读取了一道题
        Map<String, String> originalOptions = new HashMap<>();
        originalOptions.put("A", "JAVA2 EE");
        originalOptions.put("B", "JAVA2 Card");
        originalOptions.put("C", "JAVA2 ME");
        originalOptions.put("D", "JAVA2 HE");
        
        String originalKey = "D"; // 原正确答案是 D: JAVA2 HE

        System.out.println("====== 打乱前 ======");
        System.out.println("题目选项: " + originalOptions);
        System.out.println("正确答案: " + originalKey + " -> " + originalOptions.get(originalKey));

        // 2. 使用乱序器打乱选项
        Topic randomizedTopic = TopicRandomUtil.random(originalOptions, originalKey);

        // 3. 打印打乱后的结果
        System.out.println("\n====== 打乱后 ======");
        System.out.println("题目选项: " + randomizedTopic.getOption());
        System.out.println("正确答案: " + randomizedTopic.getKey() + " -> " + randomizedTopic.getOption().get(randomizedTopic.getKey()));
        
        // 验证:内容是否依然对应
        System.out.println("\n验证结果:答案内容是否改变? " + 
                (!originalOptions.get(originalKey).equals(randomizedTopic.getOption().get(randomizedTopic.getKey())) ? "改变了(错误)" : "没改变(正确)"));
    }
}

模拟运行输出结果示例:

复制代码
====== 打乱前 ======
题目选项: {A=JAVA2 EE, B=JAVA2 Card, C=JAVA2 ME, D=JAVA2 HE}
正确答案: D -> JAVA2 HE

====== 打乱后 ======
题目选项: {A=JAVA2 EE, B=JAVA2 ME, C=JAVA2 HE, D=JAVA2 Card}
正确答案: C -> JAVA2 HE

验证结果:答案内容是否改变? 没改变(正确)

总结

通过分离"标签(Key)"和"内容(Value)",利用 Collections.shuffle() 打乱标签集合,并在重新绑定的过程中通过内容比对找回正确答案的新标签。

相关推荐
nice_lcj5201 分钟前
排序(4)-归并排序专题——归并排序的分治美学
java·数据结构·算法·排序算法
阿正的梦工坊3 分钟前
【Rust】20-Rust 编译器架构与 MIR/LLVM 优化管线
开发语言·架构·rust
在放️4 分钟前
Python 爬虫 · XML、xpath 与 lxml 模块基础
开发语言·爬虫·python
我登哥MVP4 分钟前
SpringCloud 核心组件解析:服务调用和负载均衡
java·spring boot·后端·spring·spring cloud·java-ee·负载均衡
插件开发4 分钟前
英伟达cuda程序通用性关键 geforce 20xx代到最新版 在20xx上编译的c++程序可以通用吗?
java·c++·人工智能
我要打打代码5 分钟前
C# 扩展方法
开发语言·c#
JackSparrow4146 分钟前
彻底理解Java NIO(三)Java实现 I/O多路复用+Reactor模式及开源框架代码解读
java·c语言·开发语言·后端·nio·reactor模式
程序员黑豆6 分钟前
AI全栈开发 - Java:数据类型
java·前端
曹牧7 分钟前
Java:Xml中的大、小于
java·开发语言
zavoryn9 分钟前
Jackson 序列化踩坑:LocalDateTime、Long 精度丢失和 boolean isXxx 字段
java·开发语言·后端