如何使用Java Stream筛选对象列表中属性求和符合条件的子列表
嘿,这两个关于Java Stream处理子集求和的问题我很熟悉,给你一步步拆解,附上代码示例和说明:
问题1:获取对象属性求和≤阈值的子列表
首先得明确:你要的是连续的子列表还是任意组合的非连续子集?两种场景的实现方式略有不同,我都给你列出来:
场景1:连续子列表
假设我们有一个带数值属性的Item类,先定义示例类:
class Item { private int value; // 构造器和Getter public Item(int value) { this.value = value; } public int getValue() { return value; } }
接下来用Stream生成所有可能的连续子列表,筛选出属性求和≤阈值的:
import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; public class StreamSubListExample { public static void main(String[] args) { List<Item> items = Arrays.asList(new Item(2), new Item(3), new Item(1), new Item(4)); int threshold = 5; List<List<Item>> validSubLists = IntStream.range(0, items.size()) .boxed() // 对每个起始索引,生成所有可能的结束索引 .flatMap(start -> IntStream.rangeClosed(start, items.size()) .boxed() .map(end -> items.subList(start, end)) // 筛选求和≤阈值的子列表,排除空列表 .filter(subList -> !subList.isEmpty() && subList.stream().mapToInt(Item::getValue).sum() <= threshold) ) .collect(Collectors.toList()); // 打印结果验证 validSubLists.forEach(sub -> System.out.println(sub.stream().map(Item::getValue).toList())); } }
这段代码的逻辑很直观:用IntStream遍历所有起始位置,再对每个起始位置遍历结束位置,截取子列表后计算属性和,符合条件的就收集起来。
场景2:任意非连续子集
如果需要的是任意组合的子集(不要求连续),可以先通过Stream生成所有可能的子集,再筛选符合条件的:
List<List<Item>> allSubsets = items.stream() .reduce(new ArrayList<>(Collections.singletonList(new ArrayList<>())), (subsets, item) -> { List<List<Item>> newSubsets = new ArrayList<>(subsets); // 对现有每个子集,添加当前元素生成新子集 subsets.forEach(subset -> { List<Item> newSubset = new ArrayList<>(subset); newSubset.add(item); newSubsets.add(newSubset); }); return newSubsets; }, (a, b) -> { a.addAll(b); return a; }); // 筛选非空且求和≤阈值的子集 List<List<Item>> validSubsets = allSubsets.stream() .filter(subset -> !subset.isEmpty()) .filter(subset -> subset.stream().mapToInt(Item::getValue).sum() <= threshold) .collect(Collectors.toList());
⚠️ 注意:这种方法的时间复杂度是O(2^n),如果列表元素很多的话会有性能问题,适合小数据量场景;大数据量建议用动态规划优化。
问题2:获取QuestionMarks求和等于指定值的子列表
这个问题和问题1本质是一样的,只是筛选条件从"≤阈值"改成"等于目标值",我们直接用Question类来实现:
首先定义Question类:
class Question { private String questionId; private int questionMarks; // 构造器和Getter public Question(String questionId, int questionMarks) { this.questionId = questionId; this.questionMarks = questionMarks; } public int getQuestionMarks() { return questionMarks; } public String getQuestionId() { return questionId; } }
场景1:连续子列表
List<Question> questions = Arrays.asList( new Question("Q1", 2), new Question("Q2", 3), new Question("Q3", 1), new Question("Q4", 4) ); int targetSum = 5; List<List<Question>> validContinuousSubLists = IntStream.range(0, questions.size()) .boxed() .flatMap(start -> IntStream.rangeClosed(start, questions.size()) .boxed() .map(end -> questions.subList(start, end)) .filter(subList -> !subList.isEmpty() && subList.stream().mapToInt(Question::getQuestionMarks).sum() == targetSum) ) .collect(Collectors.toList());
场景2:任意非连续子集
List<List<Question>> allQuestionSubsets = questions.stream() .reduce(new ArrayList<>(Collections.singletonList(new ArrayList<>())), (subsets, q) -> { List<List<Question>> newSubsets = new ArrayList<>(subsets); subsets.forEach(subset -> { List<Question> newSubset = new ArrayList<>(subset); newSubset.add(q); newSubsets.add(newSubset); }); return newSubsets; }, (a, b) -> { a.addAll(b); return a; }); List<List<Question>> validQuestionSubsets = allQuestionSubsets.stream() .filter(subset -> !subset.isEmpty()) .filter(subset -> subset.stream().mapToInt(Question::getQuestionMarks).sum() == targetSum) .collect(Collectors.toList());
内容的提问来源于stack exchange,提问作者Vivek Ghule
相关产品推荐
相关产品推荐

