首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java 8流图缩减

Java 8流图缩减
EN

Stack Overflow用户
提问于 2015-08-13 01:47:55
回答 3查看 2.6K关注 0票数 2

我是一个全新的java 8 streams新手,我正在尝试获得下面描述的行为:

代码语言:javascript
复制
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值的总和。

例如:

代码语言:javascript
复制
xL, 10, 2015/07/07
xL, 15, 2015/07/08
xL, 20, 2015/07/07
xL, 25, 2015/07/09

结果

代码语言:javascript
复制
xL, 30, 2015/07/07

N.B. id (xL)不是重要字段。

更新--采用的解决方案(虽然不是单次通过)

代码语言:javascript
复制
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);
    }
EN

回答 3

Stack Overflow用户

发布于 2015-08-13 02:59:58

考虑到inputMap至少有一个条目,可以这样做:

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2015-08-13 12:31:13

我编写了一个自定义收集器来解决这些任务。它可以在我的StreamEx库中找到,名为MoreCollectors.maxAll(downstream)。使用它,你可以在经过一些准备工作后,在一次通过中解决任务。

首先,您的compareTo方法是错误的。它从不返回0,而且实际上它违反了约定(a.compareTo(a) == -1,这违反了自反性和反对称属性)。它很容易修复,如下所示:

代码语言:javascript
复制
@Override
public int compareTo(myVO o) {
    return o.getDate().compareTo(this.getDate());
}

接下来,我们添加一个可以根据您的需求合并两个myVO对象的myVO.merge()方法:

代码语言:javascript
复制
public myVO merge(myVO other) {
    myVO result = new myVO();
    result.setId(getId());
    result.setDate(getDate());
    result.setValue(getValue().add(other.getValue()));
    return result;
}

现在可以找到这样的结果:

代码语言:javascript
复制
Optional<myVO> result = input.stream().filter(x -> x.getValue().signum() > 0)
    .collect(MoreCollectors.maxAll(Collectors.reducing(myVO::merge)));

如果输入列表为空,则结果Optional将为空。

如果您不喜欢依赖第三方库,您可以只检查此收集器的source code,并在您的项目中编写类似的代码。

票数 1
EN

Stack Overflow用户

发布于 2015-08-18 01:36:40

如果你仔细想想,减少并不是那么复杂。您必须指定一个reduction函数,该函数:

  • 如果两个元素的日期不同,则返回日期较低的对象
  • 如果它们匹配,则创建一个包含

的值的和的结果对象

与两步法相比,它可以执行更多的add操作,当较低的日期出现在流中时,结果将被丢弃,另一方面,执行的日期比较的数量将是一半。

代码语言:javascript
复制
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函数不能修改值对象。并且您没有指定存在哪些构造函数。如果有适当的构造函数接收DateBigDecimal,则该函数几乎可以是一行程序。

请注意,如果只有一个日期最低的对象,则此方法将返回原始MyVO对象。

或者,您可以使用可变缩减,始终创建一个保存结果的新MyVO实例,但每个线程只创建一个实例,并在缩减期间修改该新实例:

代码语言:javascript
复制
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可以是单行…

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31972079

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档