如何在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
相关产品推荐
相关产品推荐

