繁体   English   中英

使用 Stream.reduce() 生成对象列表

[英]Ruducing a List of objects using Stream.reduce()

我有一个对象列表,我需要将status等于我的customizedStatus状态的对象分组到一个具有count = sumOfSameObjectsCount的自定义对象。

我们有类MyObject

class MyObject {
   Integer id;
   String name;
   String status;
   Long count;
   //constructor with attributes
   //getters 
   //setters
} 

建议实施:

List<MyObject> resultList = listOfObjects.stream()
    .collect(Collectors.groupingBy(MyObject::getStatus))
    .entrySet().stream()
    .map(e -> e.getValue().stream()
            .reduce((partialResult,nextElem) -> 
                {
                    LOGGER.info("ahaaaa! inside your reduce block ");
                    if(partialResult.getStatus().equals(customizedStatus)) {
          LOGGER.info("equal to my customizedStatus");
                        return new MyObject(customizedId, customizedName, customizedStatus, partialResult.getCount()+nextElem.getCount());
                    } else {
          LOGGER.info("not equal to my customizedStatus");
                        return new MyObject(partialResult.getId(), partialResult.getName(), partialResult.getStatus(), partialResult.getCount());
                    }
                }
            )
        )
    .map(f -> f.get())
    .collect(Collectors.toList());

万一有多个对象的status等于我的customizedStatus状态,事情就像一个魅力。
输入 :

[
                    {
                        "id": XX,
                        "name": "nameXX",
                        "status": "statusXX",
                        "count": countXX
                    },
                    {
                        "id": YY,
                        "name": "nameYY",
                        "status": "statusYY",
                        "count": countYY
                    },
                    {
                        "id": ZZ,
                        "name": "nameZZ",
                        "status": "customizedStatus",
                        "count": countZZ
                    },
                    {
                        "id": ZZz,
                        "name": "nameZZz",
                        "status": "customizedStatus",
                        "count": countZZz
                    }
                ]

输出 :

[
                    {
                        "id": XX,
                        "name": "nameXX",
                        "status": "statusXX",
                        "count": countXX
                    },
                    {
                        "id": YY,
                        "name": "nameYY",
                        "status": "statusYY",
                        "count": countYY
                    },
                    {
                        "id": customizedId,
                        "name": "customizedName",
                        "status": "customizedStatus",
                        "count": countZZ+countZZz
                    }
                ]

如果有一个对象的status等于我的customizedStatus状态,也需要对其进行自定义,不幸的是正在跳过减少块!
输入 :

[
                    {
                        "id": XX,
                        "name": "nameXX",
                        "status": "statusXX",
                        "count": countXX
                    },
                    {
                        "id": YY,
                        "name": "nameYY",
                        "status": "statusYY",
                        "count": countYY
                    },
                    {
                        "id": ZZ,
                        "name": "nameZZ",
                        "status": "customizedStatus",
                        "count": countZZ
                    }
                ]

输出 :

[
                    {
                        "id": XX,
                        "name": "nameXX",
                        "status": "statusXX",
                        "count": countXX
                    },
                    {
                        "id": YY,
                        "name": "nameYY",
                        "status": "statusYY",
                        "count": countYY
                    },
                    {
                        "id": ZZ,
                        "name": "nameZZ",
                        "status": "customizedStatus",
                        "count": countZZ
                    }
                ]

预期输出:

[
                    {
                        "id": XX,
                        "name": "nameXX",
                        "status": "statusXX",
                        "count": countXX
                    },
                    {
                        "id": YY,
                        "name": "nameYY",
                        "status": "statusYY",
                        "count": countYY
                    },
                    {
                        "id": customizedId,
                        "name": "customizedName",
                        "status": "customizedStatus",
                        "count": countZZ
                    }
                ]

如果有多个具有相同status的对象,似乎会执行reduce,如果根本没有reduce则不会执行!
使用groupByreduce获得预期输出的任何想法?

更新

结果类型不正确。 因为您没有在reduce()中提供身份,所以它将返回Optional<Object> ,但不是对象。

出于同样的原因(因为您使用了一种不需要身份的reduce() ),累加器单个元素没有影响。 文档中的引用:

使用关联累加函数对此流的元素执行归约,并返回描述归约值(如果有)的 Optional。 这相当于:

 boolean foundAny = false; T result = null; for (T element : this stream) { if (!foundAny) { foundAny = true; result = element; } else result = accumulator.apply(result, element); } return foundAny ? Optional.of(result) : Optional.empty();

第一个遇到的流元素将成为部分结果并且没有更多元素,它将被可选的原样包装并返回。

一种可能的补救措施是引入身份

public static final Integer customizedId = 99;
public static final String customizedName = "customizedName";
public static final String customizedStatus = "customizedStatus";

public static void main(String[] args) {
    List<MyObject> listOfObjects =
        List.of(new MyObject(1, "nameXX", "statusXX", 1L),
                new MyObject(2, "nameYY", "statusYY", 1L),
                new MyObject(3, "nameZZz", "customizedStatus", 3L));
    
    List<MyObject> result =
        listOfObjects.stream()
            .collect(Collectors.groupingBy(MyObject::getStatus))
            .entrySet().stream()
            .map(e -> e.getValue().stream()
                .reduce(getIdentity(e), (partialResult, nextElem) -> accumulate(partialResult, nextElem)) )
            .collect(Collectors.toList());
        
    result.forEach(System.out::println);
}

public static MyObject getIdentity(Map.Entry<String, List<MyObject>> entry) {
        
    return entry.getKey().equals(customizedStatus) ?
            new MyObject(customizedId, customizedName, customizedStatus, 0L) :
            entry.getValue().iterator().next();
}
    
public static MyObject accumulate(MyObject result, MyObject next) {
        
    return result.getStatus().equals(customizedStatus) ?
            new MyObject(customizedId, customizedName, customizedStatus, result.getCount() + next.getCount()) :
            new MyObject(result.getId(), result.getName(), result.getStatus(), result.getCount());
}

输出:

MyObject{id=2, name='nameYY', status='statusYY', count=1}
MyObject{id=1, name='nameXX', status='statusXX', count=1}
MyObject{id=99, name='customizedName', status='customizedStatus', count=3}

你可以玩这个在线演示

但是请记住,尝试将大量条件逻辑放入流中并不是最明智的想法,因为它变得更难以阅读。

下面提供的解决方案是在更新问题之前编写的,并且问题已得到澄清。 虽然,他们没有针对这个特定问题,但有人可能会从中受益,因此我会保留它们。

将列表缩减为单个对象

是否有任何解决方案使其通过减少甚至 listOfObjects 条目因状态而异?

如果您想将对象列表缩减为具有预定义idnamestatus的单个对象,则无需使用Collectors.groupingBy()创建中间映射。

如果您想为此使用reduce()操作,您可以累积count ,然后基于它创建一个结果对象:

这就是它的样子(虚拟对象的类型已更改为MyObject以避免与java.lang.Object混淆):

final Integer customizedId =    // intializing the resulting id
final String customizedName =   // intializing the resulting name
final String customizedStatus = // intializing the resulting status
        
List<MyObject> listOfObjects =  // intializing the source list
    
MyObject resultingObject = listOfObjects.stream()
    .map(MyObject::getCount)
    .reduce(Long::sum)
    .map(count -> new MyObject(customizedId,  customizedName, customizedStatus, 0L))
    .orElseThrow(); // or .orElse(() -> new MyObject(customizedId,  customizedName, customizedStatus, 0L));

实现它的另一种方法是利用MyObject是可变的这一事实,并将其用作collect()操作中的容器:

MyObject resultingObject = listOfObjects.stream()
    .collect(() -> new MyObject(customizedId,  customizedName, customizedStatus, 0L),
        (MyObject result, MyObject next) -> result.setCount(result.getCount() + next.getCount()),
        (left, right) -> left.setCount(left.getCount() + right.getCount()));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM