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.