You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot中随机抽取指定数量的Question对象

在Spring Boot中实现从Question列表随机抽取指定数量对象的方案

嘿,这个需求在项目里挺常见的,在Spring Boot中实现起来没什么难度,我给你分享几个实用的方案,你可以根据自己的场景选:

一、Java原生API实现(无额外依赖,推荐)

这个方案不需要引入任何第三方包,用JDK自带的工具类就能搞定,简单高效,适合绝大多数场景。核心思路是先把列表打乱,再截取前N个元素,或者通过随机索引选取不重复的元素。

方法1:打乱列表后截取(最常用)

这个方法的优势是代码简洁,时间复杂度低(O(n)),而且我们会创建原列表的副本,不会修改原列表的数据:

import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

@Component
public class QuestionRandomPicker {

    public List<Question> pickRandomQuestions(List<Question> allQuestions, int count) {
        // 先做参数校验,避免非法输入
        if (count <= 0 || allQuestions == null || allQuestions.size() < count) {
            throw new IllegalArgumentException("参数不合法:count必须大于0,且不能超过列表总数量");
        }

        // 创建原列表的副本,防止修改原数据
        List<Question> tempList = new ArrayList<>(allQuestions);
        // 用ThreadLocalRandom做线程安全的打乱,多线程场景下比Random更高效
        Collections.shuffle(tempList, ThreadLocalRandom.current());
        
        // 截取前count个元素返回
        return tempList.subList(0, count);
    }
}

方法2:随机索引选取(适合不想打乱列表的场景)

如果你的业务场景不希望打乱整个列表,或者列表特别大,不想创建完整的副本,可以通过随机索引来选取元素,同时记录已选索引避免重复:

public List<Question> pickRandomQuestionsWithoutShuffle(List<Question> allQuestions, int count) {
    if (count <= 0 || allQuestions == null || allQuestions.size() < count) {
        throw new IllegalArgumentException("参数不合法:count必须大于0,且不能超过列表总数量");
    }

    List<Question> result = new ArrayList<>(count);
    ThreadLocalRandom random = ThreadLocalRandom.current();
    int totalSize = allQuestions.size();
    List<Integer> selectedIndexes = new ArrayList<>(count);

    while (selectedIndexes.size() < count) {
        int index = random.nextInt(totalSize);
        // 确保不会选中重复的元素
        if (!selectedIndexes.contains(index)) {
            selectedIndexes.add(index);
            result.add(allQuestions.get(index));
        }
    }

    return result;
}

注意:这个方法在count接近列表总数量时,会有较多的重复判断,效率会下降,所以更适合count远小于列表长度的场景。

二、使用Apache Commons Collections工具类

如果你的项目已经引入了Apache Commons依赖,那么可以直接用它提供的工具方法,代码会更简洁:

首先确保依赖存在(Spring Boot项目可能已经间接引入,若没有则添加):

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

然后直接调用CollectionUtils.sample方法,它会直接返回指定数量的随机元素,内部已经处理了去重和参数校验:

import org.apache.commons.collections4.CollectionUtils;
import java.util.List;

public List<Question> pickRandomQuestionsWithCommons(List<Question> allQuestions, int count) {
    if (count <= 0 || allQuestions == null || allQuestions.size() < count) {
        throw new IllegalArgumentException("参数不合法:count必须大于0,且不能超过列表总数量");
    }
    
    // 直接获取随机样本
    return (List<Question>) CollectionUtils.sample(allQuestions, count);
}

三、使用Guava工具类

如果项目中使用Guava,也可以用它的工具方法来实现:

先添加Guava依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

然后可以用两种方式实现:

方式1:基于打乱列表(和原生思路一致,用Guava的安全随机数)

import com.google.common.base.Randoms;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public List<Question> pickRandomQuestionsWithGuava(List<Question> allQuestions, int count) {
    if (count <= 0 || allQuestions == null || allQuestions.size() < count) {
        throw new IllegalArgumentException("参数不合法:count必须大于0,且不能超过列表总数量");
    }

    List<Question> tempList = new ArrayList<>(allQuestions);
    // 使用Guava的安全随机数生成器
    Collections.shuffle(tempList, Randoms.secureRandom());
    
    return tempList.subList(0, count);
}

方式2:基于随机样本集合

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.List;

public List<Question> pickRandomQuestionsWithGuavaSample(List<Question> allQuestions, int count) {
    if (count <= 0 || allQuestions == null || allQuestions.size() < count) {
        throw new IllegalArgumentException("参数不合法:count必须大于0,且不能超过列表总数量");
    }
    
    // 注意:该方法依赖Question对象正确实现了equals和hashCode,否则会出现去重问题
    return Lists.newArrayList(Sets.randomSample(Sets.newHashSet(allQuestions), count));
}

提示:如果你的Question列表中存在重复对象,不建议用方式2,因为HashSet会自动去重,导致结果不符合预期。

一些注意事项

  • 参数校验一定要做:避免空指针、count为负数或超过列表长度的情况,提前抛出异常能快速定位问题。
  • 线程安全:多线程场景下,推荐用ThreadLocalRandom代替Random,避免线程竞争导致的性能问题。
  • 原列表不可修改:如果原列表是不可修改的(比如用Collections.unmodifiableList创建的),一定要创建副本后再操作,否则会抛出UnsupportedOperationException。

内容的提问来源于stack exchange,提问作者Hemant Pattnaik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:37:03