如何使用 Collectors.toMap 从具有列表的对象集中收集地图

How to collect map from the Set of objects that has a list using Collectors.toMap

我有 class 个带有列表的元素,我的预期输出是这样的: 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;
    }
}

您可以使用 Collectors.groupingBy instead of Collectors.toMap, along with 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<>(key, value)

平面化后,我们需要收集Map.Entry的实例。所以我使用 Collectors.groupingBy 按键对条目进行分类(我们之前存储了内部列表的每个元素,也就是您在代码中所说的 tracker )。然后,由于我们不想将 List<Map.Entry<String, Element>> 的实例作为映射的值,我们需要将流的每个 Map.Entry<String, Element> 转换为 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,非常适合您的用例。