gpt4 book ai didi

java-8 - 如何基于流分组对集合执行两个不同的功能?

转载 作者:行者123 更新时间:2023-12-04 20:34:28 25 4
gpt4 key购买 nike

我正在尝试使用流重构一些不太优雅的代码。我有一个包含字符串和 MyObjects 的 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,然后使用过滤器操作来消除空的。

下一部分是我遇到的麻烦。我尝试的第一件事是使用 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));

此链接有助于减少使用流收集器: Stream Reduction

这段代码至少有两个问题: 1) collect 是 terminal operation这将关闭流,我们还没有完成 2)我们仍然需要原始对象,但它现在已简化为 connectedToIds 的映射。

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

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

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

最终解决方案(感谢@Holger 和@Flown 的帮助)
    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);

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

最佳答案

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

要减少一些代码,您应该首先构建一个 Map<Character, Function<String, MyObject>>在映射步骤中进行简洁的查找。
看起来像这样:

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

管道是如何工作的?
  • map MyObject -> MyObject::getConnectedToId
  • 过滤空 Strings
  • serviceMap 中执行查找.如果存在,则返回 Function<String, MyObject> , 否则 id -> null
  • 过滤器 null
  • 最后一步是通过提供正确的提取器函数来收集结果

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

    也可以将计算值直接添加到 map通过使用 forEach手术。
    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));

    关于java-8 - 如何基于流分组对集合执行两个不同的功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38335358/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com