如何使用 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
,非常适合您的用例。
我有 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
,非常适合您的用例。