我在java 8流上全新,我试图获得下面描述的行为:
class myVO {
Long id;
BigDecimal value;
Date date;
getter/setter
}
myVO method(Map<Long, myVO> inputMap) {
return inputMap.stream()
.filter(x -> x.getValue().compareTo(BigDecimal.ZERO) > 0)
.sorted(); //FIXME
}
我想只获得一个myVO
obj,它是具有相同日期(最低值)的记录的BigDecimal
值的和。
e.g。
xL, 10, 2015/07/07
xL, 15, 2015/07/08
xL, 20, 2015/07/07
xL, 25, 2015/07/09
结果
xL, 30, 2015/07/07
N.B。 id(xL)不是重要的字段。
更新 - 采用的解决方案(虽然不是单程)
if(null != map && !map.isEmpty()) {
Date closestDate = map.values().stream()
.filter(t -> t.getDate() != null)
.map(MyVO::getDate)
.min(Comparator.naturalOrder()).orElse(null);
myVO.setDate(closestDate);
BigDecimal totalValue = map.values().stream()
.filter(x -> x.getValue() != null && x.getValue().signum() != 0)
.filter(t -> t.getDate().equals(closestDate))
.map(MyVO::getValue)
.reduce(BigDecimal::add).orElse(null);
myVO.setValue(totalValue != null ? totalValue.setScale(2, BigDecimal.ROUND_HALF_DOWN) : totalValue);
}
答案 0 :(得分:1)
考虑到inputMap至少有一个条目,可以这样做:
myVO method(Map<Long, myVO> inputMap) {
Date minDate = inputMap.values().stream().map(myVO::getDate).min(Comparator.naturalOrder()).get();
BigDecimal sum = inputMap.values().stream().filter(t -> t.getDate().equals(minDate)).map(myVO::getValue).reduce(BigDecimal::add).get();
myVO myVOObj = new myVO();
myVOObj.setDate(minDate);
myVOObj.setValue(sum);
myVOObj.setId(??);
return myVOObj;
}
答案 1 :(得分:1)
我写了一个自定义收藏家来解决这些任务。它位于我的StreamEx库中,名为MoreCollectors.maxAll(downstream)
。使用它,你可以在一些准备工作后单程解决任务。
首先,您的compareTo
方法是错误的。它永远不会返回0并且实际上它违反了合同(a.compareTo(a) == -1
违反了反身性和反对称属性)。它可以像这样轻松修复:
@Override
public int compareTo(myVO o) {
return o.getDate().compareTo(this.getDate());
}
接下来,让我们添加一个myVO.merge()
方法,它可以根据您的要求合并两个myVO
对象:
public myVO merge(myVO other) {
myVO result = new myVO();
result.setId(getId());
result.setDate(getDate());
result.setValue(getValue().add(other.getValue()));
return result;
}
现在结果可以这样找到:
Optional<myVO> result = input.stream().filter(x -> x.getValue().signum() > 0)
.collect(MoreCollectors.maxAll(Collectors.reducing(myVO::merge)));
如果输入列表为空,则结果Optional
将为空。
如果您不喜欢依赖第三方库,您可以查看此收藏家的source code并在项目中写下类似内容。
答案 2 :(得分:1)
如果你想一想,减少并不复杂。您必须指定一个减少函数:
与两步法相比,它可以执行更多add
次操作,当流中出现较低日期时,其结果将被删除,另一方面,执行日期比较的次数将为一半。
MyVO method(Map<Long, MyVO> inputMap) {
return inputMap.values().stream()
.reduce((a,b)->{
int cmp=a.getDate().compareTo(b.getDate());
if(cmp==0)
{
MyVO r=new MyVO();
r.setDate(a.date);
r.setValue(a.value.add(b.value));
return r;
}
return cmp<0? a: b;
}).orElse(null);
}
它看起来不简洁的主要原因是它必须创建一个新的MyVO
实例,在匹配日期的情况下保存总和,因为缩减函数不能修改值对象。而且您没有指定存在哪些构造函数。如果有一个合适的构造函数接收Date
和BigDecimal
,则该函数几乎可以是一行的。
请注意,如果只有一个具有最低日期的对象,此方法将返回原始MyVO
对象。
或者,您可以使用可变缩减,始终创建一个新的MyVO
实例来保存结果,但只为每个线程创建一个实例并在缩减期间修改该新实例:
MyVO method(Map<Long, MyVO> inputMap) {
BiConsumer<MyVO, MyVO> c=(a,b)->{
Date date = a.getDate();
int cmp=date==null? 1: date.compareTo(b.getDate());
if(cmp==0) a.setValue(a.getValue().add(b.getValue()));
else if(cmp>0)
{
a.setValue(b.getValue());
a.setDate(b.getDate());
}
};
return inputMap.values().stream().collect(()->{
MyVO r = new MyVO();
r.setValue(BigDecimal.ZERO);
return r;
}, c, c);
}
此处,如果存在适当的构造函数(或者如果初始值保证为非Supplier
null
),则BigDecimal.ZERO
可以是一行...