我有类Data10
,它有4个字段。
我有一个Data10
对象的列表。
我想用相同ID的元素的值进行算术运算。作为最终结果,我需要生成具有相同ID、相同year
、新name
("D"
(和value
的新Data10
对象(基于算术运算(。
public class Data10 {
int id;
int year;
String name;
BigDecimal value;
}
List<Data10> list = new ArrayList();
list.add(new Data10(1, 2020, "A", new BigDecimal(5.5)));
list.add(new Data10(1, 2020, "B", new BigDecimal(2.5)));
list.add(new Data10(1, 2020, "C", new BigDecimal(8.5)));
list.add(new Data10(2, 2020, "A", new BigDecimal(1.5)));
list.add(new Data10(2, 2020, "B", new BigDecimal(6.5)));
list.add(new Data10(2, 2020, "C", new BigDecimal(2.5)));
list.add(new Data10(3, 2020, "A", new BigDecimal(6.5)));
list.add(new Data10(3, 2020, "B", new BigDecimal(1.5)));
list.add(new Data10(3, 2020, "C", new BigDecimal(9.5)));
list.add(new Data10(4, 2020, "A", new BigDecimal(3.5)));
list.add(new Data10(4, 2020, "B", new BigDecimal(7.5)));
list.add(new Data10(4, 2020, "C", new BigDecimal(5.5)));
我想将以下公式应用于ID的每组:
D=A-(B*C([具有相同ID的对象组的算术运算]
我为对象名称("A|B|C"
(应用了筛选器,以便只有具有这些名称的元素才能出现在列表中。每个名称和Id只有一个唯一的对象。
我尝试过根据ID对列表进行分组,但不知道如何在每组中应用该公式。
list.stream().filter(data10 -> data10.getName().matches("A|B|C"))
.collect(Collectors.groupingBy(Data10::getId));
预期:
Data10(1, 2020, "D", -15.75); [ Formula (D= 5.5 - (2.5 * 8.5)) ]
Data10(2, 2020, "D", -14.75); [ Formula (D= 1.5 - (6.5 * 2.5)) ]
Data10(3, 2020, "D", -7.75); [ Formula (D= 6.5 - (1.5 * 9.5)) ]
Data10(4, 2020, "D", -37.75); [ Formula (D= 3.5 - (7.5 * 5.5)) ]
您可以先按id
对这些对象进行分组,然后按name
和Collectors.groupingBy
对这些对象分组,这将生成嵌套映射。然后,通过为映射中的每个值生成一个新的数据对象,将这个中间映射缩减为列表。
public static void main(String[] args) {
List<Data10> data10List = // initializing the list
List<Data10> result =
data10List.stream()
.collect(Collectors.groupingBy(Data10::getId, // by id
Collectors.groupingBy(Data10::getName))) // by name
.values().stream()
.map(map -> processValues(map))
.collect(Collectors.toList());
result.forEach(System.out::println); // printing the result
}
对于下面的方法,我假设所有组合id
和name
都是唯一的。因此,映射中的每个列表将只包含一个元素(注意:硬编码的名称以及硬编码的数学逻辑,通常必须避免(。
public static Data10 processValues(Map<String, List<Data10>> map) {
BigDecimal combinedValue =
map.get("A").get(0).getValue() // "A"
.subtract(map.get("B").get(0).getValue() // "B"
.multiply(map.get("C").get(0).getValue())); // "C"
return new Data10(map.get("A").get(0).getId(),
map.get("A").get(0).getYear(),
"D",
combinedValue);
}
为了避免对数学运算的逻辑进行硬编码,可以定义一个自定义的函数接口(JDK中没有需要三个参数的内置函数(。并通过将此接口和预期名称作为参数添加到声明中,使方法processValues()
更加通用。这样就可以在不需要更改方法的情况下应用不同的数学逻辑。
接口和实现可能是这样的:
@FunctionalInterface
interface TripleFunction<A, B, C, R> {
R calculate(A a, B b, C c);
}
TripleFunction<BigDecimal, BigDecimal, BigDecimal, BigDecimal> mathOperation =
(a, b, c) -> a.subtract(b.multiply(c));
以下是使用此功能所需的更改(在流管道和processValues
内部(:
.map(map -> processValues(map, mathOperation, "A", "B", "C"))
public static Data10 processValues(Map<String, List<Data10>> map,
TripleFunction<BigDecimal, BigDecimal, BigDecimal, BigDecimal> mathOperation,
String a, String b, String c) {
BigDecimal combinedValue = mathOperation
.calculate(map.get(a).get(0).getValue(),
map.get(b).get(0).getValue(),
map.get(c).get(0).getValue());
return new Data10(map.get("A").get(0).getId(),
map.get("A").get(0).getYear(),
"D",
combinedValue);
}
输出(问题中列出的数据用作输入(
Data10{id=1, year=2020, name='D', value=-15.75}
Data10{id=2, year=2020, name='D', value=-14.75}
Data10{id=3, year=2020, name='D', value=-7.75}
Data10{id=4, year=2020, name='D', value=-37.75}
按id对列表进行分组,然后循环遍历每个组并获得a、b和c值,将新数据添加到具有公式值的D名称的列表中。
list.stream()
.collect(Collectors.groupingBy(Data10::getId))
.entrySet().forEach(e -> {
var a = e.getValue().stream().filter(d -> d.getName().equals("A")).findFirst().get().getValue();
var b = e.getValue().stream().filter(d -> d.getName().equals("B")).findFirst().get().getValue();
var c = e.getValue().stream().filter(d -> d.getName().equals("C")).findFirst().get().getValue();
var formula = new BigDecimal(a.subtract(b.multiply(c)).doubleValue());
list.add(new Data10(e.getKey(), 2020, "D", formula));
});