Reputation: 7828
Is it possible to refactor the following for
loop using Stream
s in Java 8?
Moving the state from the inside to outside while collecting the list for different sources?
Map<StateEnum, List<List<ThreadDo>>> stateDumpListMap = new HashMap<>();
for (ThreadDumpDo dumpDo : dumpDoList) {
Map<StateEnum, List<ThreadDo>> stateDoListMap = getStateGroup(dumpDo);
stateDoListMap.entrySet().stream().forEach(entry -> {
stateDumpListMap.putIfAbsent(entry.getKey(), new ArrayList<>());
stateDumpListMap.get(entry.getKey()).add(entry.getValue());
});
}
Recently I tried several use cases but it just seems ugly to me.
I tried to use stream().map()
to achieve that, but I failed. Can anyone please help?
Upvotes: 4
Views: 153
Reputation: 1010
Try this:
Map<StateEnum, List<List<ThreadDo>>> stateDumpListMap = dumpDoList.stream()
.flatMap(dumpDo -> getStateGroup(dumpDo).entrySet().stream())
.collect(Collectors.groupingBy(
entry -> entry.getKey(),
Collectors.mapping(entry -> entry.getValue(), Collectors.toList())));
Upvotes: 1
Reputation: 5291
One more option, with collect
:
Map<StateEnum, List<List<ThreadDo>>> stateDumpListMap = dumpDoList.stream()
.map(this::getStateGroup) // dumpDo mapped to stateDoListMap ( Map<StateEnum, List<ThreadDo>> )
.map(Map::entrySet) // stream of sets of entries of stateDoListMap (Entry<StateEnum, List<ThreadDo>>)
.flatMap(Set::stream) // stream of entries of all stateDoListMaps
.collect(HashMap::new, // collect to a new HashMap, types infered from the variable declaration
(map, stateDoListMapEntry) -> map.computeIfAbsent(stateDoListMapEntry.getKey(), key -> new ArrayList<>()).add(stateDoListMapEntry.getValue()), // for each entry incoming from the stream, compute it if does not exist in the target map yet (create the top level list), then add List<ThreadDo> to it.
Map::putAll);
The three-argument collect
allows to specify exact implementation of the map it returns. It may or may not be necessary to have control over that - depending on what you need the map for.
Upvotes: 1
Reputation: 393821
Here's one way, using collect
instead of forEach
to generate the Map
, and using flatMap
to eliminate the for
loop:
Map<StateEnum, List<List<ThreadDo>>> stateDumpListMap =
dumpDoList.stream ()
.flatMap (d->getStateGroup(d).entrySet().stream ())
.collect(Collectors.toMap (Map.Entry::getKey,
e -> {
List<List<ThreadDo>> l = new ArrayList<>();
l.add (e.getValue());
return l;
},
(v1,v2)->{v1.addAll(v2);return v1;}));
Which, as Aominè commented, can be simplified to:
Map<StateEnum, List<List<ThreadDo>>> stateDumpListMap =
dumpDoList.stream ()
.flatMap (d->getStateGroup(d).entrySet().stream ())
.collect(Collectors.toMap (Map.Entry::getKey,
e -> new ArrayList<>(Collections.singletonList(e.getValue())),
(v1,v2)->{v1.addAll(v2);return v1;}));
Upvotes: 3