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

Java 8 如何基于流分组对集合执行两个不同的函数?,java-8,java-stream,Java 8,Java Stream,我正在尝试使用流重构一些不那么优雅的代码。我有一个包含字符串和MyObject的HashMap,当前使用for循环对其进行迭代,如下所示: Map<String, MyObject> map = new HashMap<>(); Map<String, MyObject> objectsToAdd = new HashMap<>(); for(MyObject object : map.values()){ String idT

我正在尝试使用流重构一些不那么优雅的代码。我有一个包含字符串和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);
此链接有助于使用流收集器减少流量:

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);
这段代码至少有两个问题:1)collect是一个将关闭流的函数,但我们还没有完成;2)我们仍然需要原始对象,但它现在已被缩减为一个connectedtoid映射

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);
Q1)是否存在允许我们根据id的第一个字符对对象进行分组的中间操作

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);
问题2)我们如何做到这一点而不将集合减少到仅包含ID

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);
问题3)最后,一旦集合被分组(将有两个),我们如何像在原始代码中那样对每个组执行单独的函数

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);

最终解决方案(感谢@Holger&@flwn的帮助)

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);
Map methodMapping=newhashmap();
methodMapping.put('i',service1::method1);
methodMapping.put('d',service2::method2);
映射到add=Map.values().stream().Map(MyObject::getConnectedToId)
.filter(StringUtils::isNotEmpty)
.map(id->methodMapping.getOrDefault(id.charAt(0),i->null).apply(id))
.filter(对象::非空)
.collect(Collectors.toMap(MyObject::getId,Function.identity(),(mo1,mo2)->mo2));
普塔尔地图(toAdd);

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

您的
方法和您的常用方法在返回类型方面非常不同。因此,我将您以前的方法转换为
API

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);
要减少一些代码,您应该首先构建一个
映射
,在映射步骤中进行简明查找。
看起来像这样:

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);
Map<Character, Function<String, MyObject>> serviceMapping = new HashMap<>();
serviceMapping.put('i', service1);
serviceMapping.put('d', service2);

还可以使用
forEach
操作将计算值直接添加到
map

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);
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));

我假设映射的键是
MyObject
id
?例如,
map.get(object.getId()).equals(object)==true
Yes,HashMap中的键是MyObject的id。为了清楚起见,我对这个问题做了一个小的编辑-idToAdd是myObject上的connectedToId,而不是对象的id。mo1和mo2是否假定为method1和method2?此外,在上一次收集操作中,我得到了“非静态方法无法从静态上下文引用”@Kristina如果已有值发生冲突,则默认的
collector::toMap
将抛出异常。因此,您必须提供一个合并函数,该函数负责决定采用哪个元素,即新值还是旧值(
(mo1,mo2)->mo2
)。@Holger在
map
操作开始之前,一切都在进行中。我不得不对
serviceMapping
进行一次修改,因为它需要返回
MyObject
的函数<代码>映射方法映射=新建HashMap();methodMapping.put('i',service1::getMOById);methodMapping.put('d',service2::getMOById)这看起来对吗?在这种情况下,似乎没有调用这些方法,相反,
map
操作返回noresults@Kristina:您没有告诉我们有关
service1
service2
的任何信息。如果它们实现相同的接口或具有定义
getMOById
方法的公共基类,则可以简化解决方案。@Holger:不幸的是,它们没有实现相同的接口或具有公共基类。每个服务返回两个不同的DTO对象,然后在返回之前用
MyObject
包装它们。