【问题标题】:Convert arraylist loop with java 8 foreach使用 java 8 foreach 转换 arraylist 循环
【发布时间】:2019-02-01 05:47:49
【问题描述】:

需要将 for 循环 (Java 6) 转换为 foreach (Java 8)

 List<CustomFormAttributeLite> custFormAttrLiteList = ArrayList ... ;
Map<String,Map<Long,Long>> customNameId = new HashMap<String, Map<Long,Long>>();
Map<Long,Long> custNameAndType = null;

for(CustomFormAttributeLite customFormAttributeLite:custFormAttrLiteList) {

    custNameAndType = new HashMap<Long,Long>();
    custNameAndType.put(customFormAttributeLite.getId(), customFormAttributeLite.getFieldType());

    customNameId.put(customFormAttributeLite.getName(), custNameAndType);
}

我正在尝试类似的东西..但不知道该怎么做

custFormAttrLiteList.forEach((customFormAttributeLite)->
                custNameAndType = new HashMap<Long,Long>();
                custNameAndType.put(customFormAttributeLite.getId(), customFormAttributeLite.getFieldType());

                customNameId.put(customFormAttributeLite.getName(), custNameAndType);
            );

【问题讨论】:

  • 这些是您正在使用的 lambda,这就是您应该寻找的,您几乎得到了每个权利。
  • 最好使用groupingBy

标签: java arraylist java-8


【解决方案1】:

您也可以为此使用Collectors#groupingBy。首先按名称字段分组,然后按 id 和 fieldType 分组。

List<CustomFormAttributeLite> custFormAttrLiteList = new ArrayList<>();

Map<String,Map<Long,Long>> customNameId = custFormAttrLiteList.stream()
                        .collect(Collectors.groupingBy(CustomFormAttributeLite::getName,
                         Collectors.toMap(CustomFormAttributeLite::getId, CustomFormAttributeLite::getFieldType)));

如果名称不是唯一的,则结果将与您期望的不同,因此在这种情况下,我们需要使用 Collectors.toMap 并使用 mergeFunction 仅保留第二个非唯一条目:

Map<String,Map<Long,Long>> customNameIdNonUnique = custFormAttrLiteList.stream()
                    .collect(Collectors.toMap(CustomFormAttributeLite::getName, //key mapper function
                     (obj) -> {Map<Long,Long> map = new HashMap<>(); map.put(obj.getId(), obj.getFieldType()); return map;}, //value mapper function
                     (key1, key2)-> key2)); //retaining only the second entry 

作为测试,我使用了以下数据集来测试这两种解决方案:

CustomFormAttributeLite c1 = new CustomFormAttributeLite("foo", 123L, 123L);
CustomFormAttributeLite c2 = new CustomFormAttributeLite("foo", 124L, 125L);
CustomFormAttributeLite c3 = new CustomFormAttributeLite("bar", 125L, 126L);
CustomFormAttributeLite c4 = new CustomFormAttributeLite("bar", 125L, 126L);

第二个解决方案产生了输出:

{bar={125=126}, foo={124=125}}

【讨论】:

  • 你也可以使用obj -&gt; Collections.singletonMap(obj.getId(), obj.getFieldType())作为值映射函数。
【解决方案2】:

您只能在 for-each lambda 表达式中使用最终变量。所以试试这样:

final Map<String,Map<Long,Long>> customNameId = new HashMap<String, Map<Long,Long>>();
custFormAttrLiteList.forEach((customFormAttributeLite)-> {
                Map<Long,Long> custNameAndType = new HashMap<Long,Long>();
                custNameAndType.put(customFormAttributeLite.getId(), customFormAttributeLite.getFieldType());

                customNameId.put(customFormAttributeLite.getName(), custNameAndType);
            });

【讨论】:

  • +1,我要补充一点,不需要将它们显式标记为final,只是它们实际上是最终的,这意味着 lambda 不能为变量分配新值。跨度>
  • 是的,现在很好
【解决方案3】:

我强烈建议您在forEach避免改变集合状态。请改用.collect(...) 收集数据。

首先创建一个用于将CustomFormAttributeLite 映射到Map&lt;Long,Long&gt; 的方法(静态动态):

Map<Long,Long> mapToNameAndType(CustomFormAttributeLite attribute){
    Map<Long, Long> map = new HashMap<>();
    map.put(attribute.getId(), attribute.getFieldType());
    return map;
}

然后您可以使用Collectors.toMap() 将数据简单地收集到一个新的Map 中:

Map<String, Map<Long, Long>> customNameId = 
    custFormAttrLiteList.stream()                                                                         
                        .collect(toMap(CustomFormAttributeLite::getName,this::mapToNameAndType));

【讨论】:

  • list.forEach != list.stream().forEach,使用第一个变异没什么不好,这就是 OP 所做的。
猜你喜欢
  • 1970-01-01
  • 2013-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-19
相关资源
最近更新 更多