to answer this question, we can first see why the result of Map < String, List < List < String >
appears. This depends on the design semantics of Collectors.groupingBy
. It represents a way to classify and group stream data according to certain rules, and to provide a method of how to collect the same group of data, so this is the meaning of Collectors.groupingBy
two parameters
.
the first parameter of the subject is Condition::getCondName
, and the Condition
representing the flow is grouped according to its condName
attribute. After grouping, the Condition
of the same group is handled. Here the main Collectors.mapping (Condition::getCondValue, Collectors.toList ()
, mapping
represents the mapping transformation. Here it is somewhat similar to grouping. Collectors.mapping
the first parameter sets the Condition
of the stream. At this point, the stream is List < String >
, followed by the second parameter Collectors.toList ()
, oh, of course, the final result is List < List < String > >
.
the reason why the subject can't get the answer here is that the second parameter of Collectors.mapping
is not written correctly. I can think of three ways of
the first: still use Collectors.mapping
, similar to the one mentioned by the subject, go over it again,
.
Map<String, List<String>> collect = conditions.stream()
.collect(Collectors.groupingBy(Condition::getCondName,
Collectors.mapping(Condition::getCondValue,
Collectors.collectingAndThen(Collectors.toList(), lists -> lists.stream().flatMap(List::stream).collect(Collectors.toList())))));
here the second parameter of Collectors.mapping
uses Collectors.collectingAndThen
, which can be seen by the name. The first parameter is, of course, according to Collectors.toList ()
. After collection, the second parameter traverses List
again, flattens it and then forms List
.
emmm, I don't like it very much either, but it just leads to collectingAndThen
. Maybe the subject can use it
in the future.
second: also use Collectors.mapping
, but this time the second parameter uses Collectors.reducing
Map<String, List<String>> collect2 = conditions.stream()
.collect(Collectors.groupingBy(Condition::getCondName,
Collectors.mapping(Condition::getCondValue,
Collectors.reducing(new ArrayList<>(), (l1, l2) -> Stream.concat(l1.stream(), l2.stream()).collect(Collectors.toList())))));
here Collectors.reducing
is the aggregation of data, which coincides with the current scenario. When the data in the stream is transformed from Condition
to List < String >
, we want to find a way to merge different List < String >
. Therefore, this aggregation method Collectors.reducing
is used here. The first parameter is the starting value, and the second parameter represents how to merge the two list
.
the second method is better, but here I also think of the third method
third: instead of Collectors.groupingBy
, use Collectors.toMap
Map<String, List<String>> collect1 = conditions.stream().collect(
Collectors.toMap(Condition::getCondName, Condition::getCondValue, (c1, c2) -> Stream.concat(c1.stream(), c2.stream()).collect(Collectors.toList())));
the third way feels a bit simpler than the first two, but it skillfully uses the toMap
method. The toMap
method is generally used only when the data can have an one-to-one relationship. Most of the time, we only use the two-parameter method, that is, pass in how to get key
of map
and how to get the two Function of value
of map
. In business, data can be guaranteed to have an one-to-one relationship. If only two-parameter methods are called, but one-to-many cases do occur during actual use, then calling toMap
two-parameter methods will report an error, so there is the toMap
three-parameter method. The third parameter represents how to combine the value
values of the same key
, so this is similar to the idea of the second method. Merge two sets
above is my answer, for reference only
by the way, plus, I generally don't like to write excessively long lambda expressions in the stream, because the flow is supposed to reflect the current process, not all the bad things go down, so the third way, the final collection merge, had better be written as a BinaryOperator
, after all, the method can also be a parameter or attribute
.
public static final BinaryOperator<List<String>> listMergeMethod = (l1, l2) -> Stream.concat(l1.stream(), l2.stream()).collect(Collectors.toList());
Map<String, List<String>> collect1 = conditions.stream().collect(
Collectors.toMap(Condition::getCondName, Condition::getCondValue, listMergeMethod));
it makes me feel better.