【问题标题】:Java 8 way to convert collection into one key multiple values mapJava 8 将集合转换为一键多值映射的方法
【发布时间】:2016-02-28 10:26:07
【问题描述】:

使用 Java 8 执行以下代码的任何方式。

        final Map<String, Collection<ProductStrAttributeOverrideRulesModel>> attributeRulesMap = new HashMap<String, Collection<ProductStrAttributeOverrideRulesModel>>();
        for (final ProductStrAttributeOverrideRulesModel rule : rules)
        {
            final String key = rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier();
            if (attributeRulesMap.containsKey(key))
            {
                final Collection<ProductStrAttributeOverrideRulesModel> currentRules = attributeRulesMap.get(key);
                currentRules.add(rule);
            }
            else
            {
                final Collection<ProductStrAttributeOverrideRulesModel> list = new LinkedList<ProductStrAttributeOverrideRulesModel>();
                list.add(rule);
                attributeRulesMap.put(key, list);
            }
        }

如果只是

 final Map<String, ProductStrAttributeOverrideRulesModel> attributeRulesMap

我可以像下面那样做,但我需要根据键将整个集合安排在地图中,并且每个键可以有多个值存储在集合中。

Map<String, ProductStrAttributeOverrideRulesModel> result =
choices.stream().collect(Collectors.toMap(ProductStrAttributeOverrideRulesModel::getProductStrAttributeOverride.getProductStrTypeField.getAttributeDescriptorQualifier,
                                          Function.identity()));

【问题讨论】:

  • 对于 Java 8 之前的代码,这有点反模式。当您可以简单地使用一个时,您使用两个映射查找(containsKeyget)。这使得代码可能会慢很多。
  • 你提到了一个很好的观点鲍里斯。非常感谢。
  • @BoristheSpider 如果有一对("something", null)containsKey("something") 将返回true,但get("something") 将返回null - 结果与根本没有这样的密钥相同.
  • @JaroslawPawlak 我知道Map 是如何工作的。在这种情况下,逻辑是相同的,因此不需要 - 这是浪费 CPU 周期。
  • @Jaroslaw Pawlak:很明显,如果根据@987654333 将映射到null 的当前映射视为“当前”,这将 有帮助@,当 Collection 预计将无条件添加一个元素时。真正需要尊重到null 的映射的情况很少见,即便如此,说value=map.get(key); if(value==null &amp;&amp; !map.containsKey(key)) … 更有效。

标签: java dictionary collections java-8


【解决方案1】:

你可以使用groupingBy

Map<String,List<ProductStrAttributeOverrideRulesModel>>
    map = 
        choices.stream()
               .collect(Collectors.groupingBy(rule -> rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier()));

如果您不想要List,您可以将第二个参数传递给groupingBy 并指定您想要的任何Collection。例如:

Map<String,Collection<ProductStrAttributeOverrideRulesModel>>
    map =
        choices.stream()
               .collect(Collectors.groupingBy(rule -> rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier(),
                                              Collectors.toCollection(HashSet::new)));

【讨论】:

  • 最终地图> map = rules.stream().collect(Collectors.groupingBy( rule -> rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier())) ;给我以下错误类型不匹配:无法从 Map> 转换为 Map>
  • @SaurabhKumar getAttributeDescriptorQualifier() 方法是否返回 String
  • 最终字符串键 = rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier();我也在代码中发布了
  • @SaurabhKumar 也许这是一些编译器问题。试试rule -&gt; (String) rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier()。顺便说一句,choicesrules 是同一类型吗?您的第一个 sn-p 使用 rules 集合,而第二个使用 choices
  • @SaurabhKumar 使用mapping 收集器将对象从一种类型映射到另一种类型: ...collect(Collectors.groupingBy(rule -> rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier( ),Collectors.mapping(StringAttributeOverrideValueGetter::new,Collectors.toList())));
【解决方案2】:

请注意,它并不总是必须是 Stream 操作。您的代码还将受益于使用“菱形运算符”(尽管对 Java 8 来说不是新的)和使用新的集合操作,即computeIfAbsent,它允许省略循环内的整个条件及其代码重复。将两者放在一起,您将得到:

final Map<String, Collection<ProductStrAttributeOverrideRulesModel>>
                                                   attributeRulesMap = new HashMap<>();
for(final ProductStrAttributeOverrideRulesModel rule: rules)
{
    final String key = rule.getProductStrAttributeOverride()
        .getProductStrTypeField().getAttributeDescriptorQualifier();
    attributeRulesMap.computeIfAbsent(key, x->new LinkedList<>()).add(rule);
}

如果您愿意,也可以用 forEach 调用替换循环:

rules.forEach(rule -> attributeRulesMap.computeIfAbsent(
    rule.getProductStrAttributeOverride()
        .getProductStrTypeField().getAttributeDescriptorQualifier(),
    x->new LinkedList<>()).add(rule)
);

虽然这是否是对这里的经典循环的改进还有待商榷......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 2019-08-15
    相关资源
    最近更新 更多