【问题标题】:How to collect map from the Set of objects that has a list using Collectors.toMap如何使用 Collectors.toMap 从具有列表的对象集中收集地图
【发布时间】:2018-03-21 18:09:02
【问题描述】:

我有一个带有列表的类 Element,我的预期输出是这样的: Map<String , List<Element>>

{
    1 = [Element3, Element1],
    2 = [Element2, Element1],
    3 = [Element2, Element1], 4=[Element2]
}

我的输入是一组元素对象,我使用forEach 来获得所需的结果,但我正在寻找如何使用collectors.toMap 收集它。任何意见都非常感谢

Set<Element> changes = new HashSet();

List<String> interesetList = new ArrayList();
interesetList.add("1");
interesetList.add("2");
interesetList.add("3");

Element element = new Element(interesetList);
changes.add(element);

interesetList = new ArrayList();
interesetList.add("2");
interesetList.add("3");
interesetList.add("4");

element = new Element(interesetList);
changes.add(element);

Map<String, List<Element>> collect2 = new HashMap();

changes.forEach(element -> {
    element.getInterestedList().forEach(tracker -> {
        collect2.compute(tracker, ( key , val) -> {
            List<Element> elementList = val == null ? new ArrayList<Element>() : val;
            elementList.add(Element);
            return elementList;
        }); 
    });
});


class Element {

    List<String> interestedList;
    static AtomicInteger sequencer = new AtomicInteger(0);
    String mName;
    public Element(List<String> aList) {
        interestedList = aList;
        mName = "Element" + sequencer.incrementAndGet();
    }
    public List<String> getInterestedList() {
        return interestedList;
    }
    @Override
    public String toString() {
        return mName;
    }
}

【问题讨论】:

    标签: java-8 java-stream collectors


    【解决方案1】:

    您可以使用Collectors.groupingBy 而不是Collectors.toMap 以及Collectors.mapping 来做到这一点,它使一个收集器适应另一个收集器:

    Map<String, List<Element>> result = changes.stream()
        .flatMap(e -> e.getInterestedList().stream().map(t -> Map.entry(t, e)))
        .collect(Collectors.groupingBy(
            Map.Entry::getKey, 
            Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
    

    您需要先使用Stream.flatMap 方法,然后将内部列表的元素与当前Element 实例配对。我通过新的 Java 9 的 Map.entry(key, value) 方法做到了这一点。如果您还没有使用 Java 9,可以将其更改为 new AbstractMap.SimpleEntry&lt;&gt;(key, value)

    平面映射后,我们需要收集Map.Entry 的实例。所以我使用Collectors.groupingBy 来按键分类条目(我们之前存储了内部列表的每个元素,也就是你在代码中调用的tracker)。然后,由于我们不想将 List&lt;Map.Entry&lt;String, Element&gt;&gt; 的实例作为映射的值,我们需要将流的每个 Map.Entry&lt;String, Element&gt; 转换为 Element(这就是我使用 Map.Entry::getValue 作为Collectors.mapping 的第一个参数)。我们还需要指定一个下游收集器(此处为Collectors.toList()),以便外部Collectors.groupingBy 收集器知道将属于每个组的所有已适应流的元素放置在哪里。


    一种更短且肯定更有效的方法(类似于您的尝试)可能是:

    Map<String, List<Element>> result = new HashMap<>();
    changes.forEach(e ->
        e.getInterestedList().forEach(t ->
            result.computeIfAbsent(t, k -> new ArrayList<>()).add(e)));
    

    这使用Map.computeIfAbsent,非常适合您的用例。

    【讨论】:

    • 非常感谢您的全面解释和优雅的解决方案。非常有帮助 - 请您分享您的博客,以便我可以关注您。再次感谢您。
    • @PathbreakersINDIA 是boundsofjava.com,感谢您的支持!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    • 2017-09-26
    相关资源
    最近更新 更多