当前位置: 首页 > 知识库问答 >
问题:

如何基于流分组对集合执行两个不同的函数?

於乐
2023-03-14

我试图使用流重构一些不那么优雅的代码。我有一个包含字符串和MyObject的HashMap,目前正在使用for循环对其进行迭代,如下所示:

Map<String, MyObject> map = new HashMap<>();
Map<String, MyObject> objectsToAdd = new HashMap<>();


for(MyObject object : map.values()){
        String idToAdd = object.getConnectedToId();

        if(StringUtils.isEmpty(idToAdd) {
            continue;
        }

        if(idToAdd.substring(0,1).equals("i")){ // connected to an ICS
            MyObject newObject = service1.someMethod(idToAdd);

            if(newObject != null) {
                objectsToAdd.put(newObject.getId(), newObject);
            }
        } else if (idToAdd.substring(0,1).equals("d")){ // connected to a device
            MyObject newObject = service2.someMethod(idToAdd);
            if(newObject != null) {
                objectsToAdd.put(newObject.getId(), newObject);
            }
        }

    }

    map.putAll(objectsToAdd);

因为我只关心id,所以我首先使用map操作只获取id,然后使用filter操作来消除空的id。

下一部分是我遇到的问题。我尝试的第一件事是使用Collectors groupingBy操作,这样我可以根据id的第一个字符对项目进行分组,结果是:

        map.values().stream()
            .map(myObject -> myObject.getConnectedToId()) // get a map of all the ids
            .filter(StringUtils::isNotEmpty) // filter non empty ones
            .collect(
                Collectors.mapping(
                    MyObject::getId,
                    Collectors.toList())),
                        Collectors.groupingBy(
                            s -> s.substring(0,1));

此链接有助于使用流收集器进行减少:流减少

我们对这段代码至少有两个问题:1)收集是一个终端操作,它将关闭流,我们还没有完成;2)我们仍然需要原始对象,但它现在已经被简化为连接的ToIds映射。

Q1)是否有中间操作允许我们根据id的第一个字符对对象进行分组?

Q2)我们如何在不将收集减少到只有id的情况下做到这一点?

Q3)最后,一旦集合被分组(将有两个),我们如何像原始代码一样对每个组执行单独的功能?

最终解决方案(得益于@Holger

    Map<Character, Function<String, MyObejct>> methodMapping = new HashMap<>();
    methodMapping.put('i', service1::method1);
    methodMapping.put('d', service2::method2);

    Map<String, MyObject> toAdd = map.values().stream().map(MyObject::getConnectedToId)
        .filter(StringUtils::isNotEmpty)
        .map(id -> methodMapping.getOrDefault(id.charAt(0), i -> null).apply(id))
        .filter(Objects::nonNull)
        .collect(Collectors.toMap(MyObject::getId, Function.identity(), (mo1, mo2) -> mo2));

    map.putAll(toAdd);

为避免并发修改异常,在执行流操作时,需要先将对象存储在临时映射中,然后在完成后将其添加到最终映射中。

共有1个答案

池兴邦
2023-03-14

就返回类型而言,您的< code>Stream方法和您的通用方法非常不同。因此,我将您以前的方法转变为< code>Stream API。

要减少一些代码,您应该首先构建一个< code >映射

Map<Character, Function<String, MyObject>> serviceMapping = new HashMap<>();
serviceMapping.put('i', service1);
serviceMapping.put('d', service2);

管道是如何工作的?

    < li >映射< code >我的对象 -
Map<String, MyObject> toAdd = map.values().stream().map(MyObject::getConnectedToId)
    .filter(StringUtils::isEmpty)
    .map(id -> serviceMapping.getOrDefault(id.charAt(0), i -> null).apply(id))
    .filter(Objects::nonNull)
    .collect(Collectors.toMap(MyObject::getId, Function.identity(), (mo1, mo2) -> mo2));
map.putAll(toAdd);

也可以通过使用< code>forEach操作将计算值直接添加到< code>map中。

map.values().stream().map(MyObject::getConnectedToId)
    .filter(StringUtils::isEmpty)
    .map(id -> serviceMapping.getOrDefault(id.charAt(0), i -> null).apply(id))
    .filter(Objects::nonNull)
    .forEach(mo -> map.put(mo.getId(), mo));
 类似资料:
  • 我有2%s: 我需要这样的联合: 函数不起作用,因为列的编号和名称不同。 我怎么能这么做?

  • 问题内容: 假设我有两个类CLassA和CLassB。它们有一个共同的属性,例如每个类拥有的元素数量。 我如何从ClassA和CLassB的对象创建一个集合,并按该属性排序(降序升序无所谓)? 我收集了一个类型,但是当我尝试实现Comparable Interface时,我无法使用该方法(例如,获取返回元素nr的get)。 我有什么解决方案? 谢谢你的帮助! 问题答案: 实际上,如果要将它们放在同

  • 问题内容: 我有一堂课,有两个日期字段说: 我想根据日期对上述类别的列表进行排序,如果它们相等,则根据max(activation)和max(timeStamp)进行排序。 我尝试的代码如下所示,仅获取max(激活) 任何帮助将不胜感激。 谢谢 问题答案: 这样就可以了!

  • 我有两个数组: 阵列 1: 和阵列 2: 我需要根据合并这两个数组并得到这个: 不通过< code>Object.keys进行迭代,我如何做到这一点?

  • 问题内容: 我有一张有5列的桌子。当我列出表时,我想按一列排序,以便将类型分组在一起,然后按字母顺序对它们进行排序,以便于查找。 是否可以通过两个不同的列? 这是我当前的选择: 我想我正在寻找的是这样的: 问题答案: 您可以通过列名或返回的列号指定ORDER BY。 因此,您可以执行以下操作: 或类似的东西: 您还可以进行升序和降序。因此,如果您想使用“类型降序”但“标题升序”:

  • 我有一个问题。 我想做的是迭代tmpList,找到身高和体重的总和,并在名称为空时添加“No Name”。 我已经工作到目前为止,这似乎是不对的。我是说我必须重复每个案例。 有没有办法把它们结合在一起?或者有什么更好的建议?