I have an object
public class Sample {
private String Id;
...
private String acct;
private BigDecimal amt1;
private BigDecimal amt3;
}
I have a list
Id | acct | amt1 | amt2
1 | 1111 | 0.50 | 0.50
2 | 1112 | 0.50 | 0.50
I want to sum amt1 and amt2 from a list
Map<Object, Sample> map = collectedTxs.stream()
.collect(Collectors.toMap(
f -> f.getAcct(),
Function.identity(),
(s, a) -> new Sample(
s.getId(),
s.getAcct(),
s.getAmt1().add(a.getAmt1()),
s.getAmt2().add(a.getAmt2()),
), LinkedHashMap::new));
Is there any better way to do it? I don't want to reassign the result to another Sample object.
I don't want to reassign the result to another Sample object.
Streams are meant to be used to map values. Modifying existing state outside of the stream environment is ill advised as it can cause data inconsistencies in certain situations. But you can still do it very efficiently using Java 8+ map features. (Streams is not always the best or most efficient way of manipulating data. There is nothing wrong with using loops.)
Here is how I would do it. Here is the class definition with the only changes being.
static class Sample {
private String Id;
private String acct;
private BigDecimal amt1;
private BigDecimal amt2;
public Sample(String id, String acct, BigDecimal amt1,
BigDecimal amt2) {
Id = id;
this.acct = acct;
this.amt1 = amt1;
this.amt2 = amt2;
}
public Sample(Sample sample) {
this(sample.Id, sample.acct,sample.amt1,sample.amt2);
}
public static Sample copyOf(Sample s) {
return new Sample(s.Id, s.acct, s.amt1,s.amt2);
}
public Sample add(Sample sample) {
amt1 = amt1.add(sample.amt1);
amt2 = amt2.add(sample.amt2);
return this;
}
// other getters and setters
public void setId(String id) {
Id = id;
}
public void setAcct(String acct) {
this.acct = acct;
}
public void setAmt1(BigDecimal amt1) {
this.amt1 = amt1;
}
public void setAmt2(BigDecimal amt2) {
this.amt2 = amt2;
}
public String getId() {
return Id;
}
public String getAcct() {
return acct;
}
public BigDecimal getAmt1() {
return amt1;
}
public BigDecimal getAmt2() {
return amt2;
}
@Override
public String toString() {
return String.format("[%s, %s, %s, %s]", Id, acct, amt1,
amt2);
}
}
The Data
List<Sample> collectedTxs = List.of(
new Sample("1", "1111", new BigDecimal(".50"),
new BigDecimal(".50")),
new Sample("2", "1112", new BigDecimal(".50"),
new BigDecimal(".50")),
new Sample("2", "1112", new BigDecimal("1.50"),
new BigDecimal("2.50")),
new Sample("1", "1111", new BigDecimal("4.50"),
new BigDecimal("8.50")));
The Process
s
is the first encountered instance of each account.BiFunction
combines them via add using method referenceMap<String,Sample> map = new HashMap<>();
for (Sample s : collectedTxs) {
map.merge(s.getAcct(), s, Sample::add);
}
map.forEach((k,v)-> System.out.println(k + " " + v);
prints
1112 [2, 1112, 2.00, 3.00]
1111 [1, 1111, 5.00, 9.00]
Here is the original data. Note that the first accounts encountered in the iteration were changed.
collectedTxs.forEach(System.out::println);
[1, 1111, 5.00, 9.00]
[2, 1112, 2.00, 3.00]
[2, 1112, 1.50, 2.50]
[1, 1111, 4.50, 8.50]
If you want to preserve the original instances. Then make the following change in the map construct.
map.merge(s.getAcct(), Sample.copyOf(s), Sample::add);