如何对列表中具有相同ID的每个元素组应用算术运算



我有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对这些对象进行分组,然后按nameCollectors.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
}

对于下面的方法,我假设所有组合idname都是唯一的。因此,映射中的每个列表将只包含一个元素(注意:硬编码的名称以及硬编码的数学逻辑,通常必须避免(。

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));
});

最新更新