如何使用Java Streams按类型分组求和并计算类型间差值
Got it, let's break down how to solve this problem using Java Streams. You want to group your Sample objects by SampleTypeId, sum up the quantities for ADD and SUBTRACT types within each group, then compute the difference (I’m assuming that’s total ADD minus total SUBTRACT—adjust if needed!).
First, let’s define a simple Sample class to work with (I’ll include the enum for SampleType too):
class Sample { private int sampleId; private int sampleTypeId; private int sampleQuantity; private SampleType sampleType; // Constructor, getters, and toString (omitted for brevity) public enum SampleType { ADD, SUBTRACT } // Example constructor for testing public Sample(int sampleId, int sampleTypeId, int sampleQuantity, SampleType sampleType) { this.sampleId = sampleId; this.sampleTypeId = sampleTypeId; this.sampleQuantity = sampleQuantity; this.sampleType = sampleType; } // Getters public int getSampleTypeId() { return sampleTypeId; } public int getSampleQuantity() { return sampleQuantity; } public SampleType getSampleType() { return sampleType; } }
Option 1: Concise One-Step Solution (Most Efficient)
This approach combines grouping and difference calculation into a single Stream operation. Instead of first summing each type separately, we treat SUBTRACT quantities as negative values and sum everything up directly:
import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class SampleProcessor { public static void main(String[] args) { List<Sample> samples = List.of( new Sample(1, 1, 5, Sample.SampleType.ADD), new Sample(2, 1, 15, Sample.SampleType.ADD), new Sample(3, 1, 25, Sample.SampleType.ADD), new Sample(4, 1, 5, Sample.SampleType.SUBTRACT), new Sample(5, 1, 25, Sample.SampleType.SUBTRACT), new Sample(6, 2, 10, Sample.SampleType.ADD), new Sample(7, 2, 3, Sample.SampleType.SUBTRACT) ); // Group by SampleTypeId, calculate ADD - SUBTRACT difference in one step Map<Integer, Integer> typeIdToDifference = samples.stream() .collect(Collectors.groupingBy( Sample::getSampleTypeId, Collectors.summingInt(sample -> sample.getSampleType() == Sample.SampleType.ADD ? sample.getSampleQuantity() : -sample.getSampleQuantity() ) )); // Output the results typeIdToDifference.forEach((id, diff) -> System.out.printf("SampleTypeId %d: Net Difference = %d%n", id, diff) ); } }
Output:
SampleTypeId 1: Net Difference = 15 SampleTypeId 2: Net Difference = 7
How it works:
- For each
Sample, we map its quantity to a positive value if it’sADD, or a negative value if it’sSUBTRACT. - The
summingIntcollector then adds all these values together for eachSampleTypeIdgroup. The result is exactly(Total ADD) - (Total SUBTRACT). - This is the most efficient method because it only requires one pass through the Stream.
Option 2: Explicit Two-Step Approach (More Transparent)
If you prefer to first see the individual sums for ADD and SUBTRACT before calculating the difference, use this nested grouping approach:
// Step 1: Group by SampleTypeId, then by SampleType, summing quantities Map<Integer, Map<Sample.SampleType, Integer>> typeIdToTypeSums = samples.stream() .collect(Collectors.groupingBy( Sample::getSampleTypeId, Collectors.groupingBy( Sample::getSampleType, Collectors.summingInt(Sample::getSampleQuantity) ) )); // Step 2: Calculate the difference for each SampleTypeId Map<Integer, Integer> typeIdToDifference = typeIdToTypeSums.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, entry -> { int addTotal = entry.getValue().getOrDefault(Sample.SampleType.ADD, 0); int subtractTotal = entry.getValue().getOrDefault(Sample.SampleType.SUBTRACT, 0); return addTotal - subtractTotal; } ));
How it works:
- First, we create a nested map:
SampleTypeId→SampleType→ Total Quantity. - Then, we iterate over this nested map, using
getOrDefaultto handle cases where a group might not have anyADDorSUBTRACTentries (defaulting to 0 if missing). - This approach is more readable if you need to inspect the individual sums later, but it requires two passes through the data.
Handling Other Sample Types
If your SampleType enum has additional values you want to ignore, adjust the mapping function in Option 1 like this:
Collectors.summingInt(sample -> { if (sample.getSampleType() == Sample.SampleType.ADD) { return sample.getSampleQuantity(); } else if (sample.getSampleType() == Sample.SampleType.SUBTRACT) { return -sample.getSampleQuantity(); } else { return 0; // Ignore other types entirely } })
内容的提问来源于stack exchange,提问作者user1778855

