我是一个全新的java 8 streams新手,我正在尝试获得下面描述的行为:
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值的总和。
例如:
xL, 10, 2015/07/07
xL, 15, 2015/07/08
xL, 20, 2015/07/07
xL, 25, 2015/07/09结果
xL, 30, 2015/07/07N.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);
}发布于 2015-08-13 02:59:58
考虑到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;
}发布于 2015-08-13 12:31:13
我编写了一个自定义收集器来解决这些任务。它可以在我的StreamEx库中找到,名为MoreCollectors.maxAll(downstream)。使用它,你可以在经过一些准备工作后,在一次通过中解决任务。
首先,您的compareTo方法是错误的。它从不返回0,而且实际上它违反了约定(a.compareTo(a) == -1,这违反了自反性和反对称属性)。它很容易修复,如下所示:
@Override
public int compareTo(myVO o) {
return o.getDate().compareTo(this.getDate());
}接下来,我们添加一个可以根据您的需求合并两个myVO对象的myVO.merge()方法:
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,并在您的项目中编写类似的代码。
发布于 2015-08-18 01:36:40
如果你仔细想想,减少并不是那么复杂。您必须指定一个reduction函数,该函数:
的值的和的结果对象
与两步法相比,它可以执行更多的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实例,在匹配日期的情况下保存总和,因为reduction函数不能修改值对象。并且您没有指定存在哪些构造函数。如果有适当的构造函数接收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);
}在这里,如果存在适当的构造函数(或者如果保证初始值是非nullBigDecimal.ZERO),则Supplier可以是单行…
https://stackoverflow.com/questions/31972079
复制相似问题