Java 流:groupingBy 和 flatMapping 键
Java streams: groupingBy and flatMapping keys
假设我有一个国家/地区对象列表,其中包含该国家/地区使用的语言列表,如下所示:
class Country {
List<String> languages;
}
我想创建以下格式的地图:
Map<String, List<Country>>
,这样每种语言都映射到国家/地区对象列表。例如:
"French" -> [Country:France, Country:Canada],
"English" -> [Country:UK, Country:US]
性能是这里的一个问题,所以我想避免多次迭代和查找。我试过使用 groupingBy
,但如何 flatMap
键集?
例如,结果为 Map<List<String>, List<Country>>
:
countries.stream()
.collect(Collectors.groupingBy(country -> country.getLanguages(), toList()));
这样就可以了:
countries.stream()
.flatMap(country -> country.getLanguages()
.stream()
.map(lang -> new SimpleEntry<>(lang,
new ArrayList<>(Arrays.asList(country)))))
.collect(Collectors.toMap(
Entry::getKey,
Entry::getValue,
(l1, l2) -> {
l1.addAll(l2);
return l2;
}));
由于您似乎很在意性能,所以不要将流用于这个简单的任务:
Map<String, List<Country>> countriesByLanguage = new HashMap<>();
for (Country country : countries) {
for (String language : country.getLanguages()) {
countriesByLanguage.computeIfAbsent(language, k -> new ArrayList<>())
.add(country);
}
}
您可以使用 流中的流 来实现,如下所示:首先遍历国家列表,然后遍历嵌套的语言列表并准备 «language, country»
对,然后收集起来映射:
public static void main(String[] args) {
List<Country> countries = List.of(
new Country("France", List.of("French")),
new Country("Canada", List.of("French")),
new Country("UK", List.of("English")),
new Country("US", List.of("English")));
Map<String, List<Country>> map = countries.stream()
// Stream<Map.Entry<String,Country>>
.flatMap(country -> country.getLanguages().stream()
.map(lang -> Map.entry(lang, country)))
.collect(Collectors.toMap(
// key - language
Map.Entry::getKey,
// value - List<Country>
entry -> new ArrayList<>(List.of(entry.getValue())),
// merge duplicates, if any
(list1, list2) -> {
list1.addAll(list2);
return list1;
}
));
// output
map.forEach((k, v) -> System.out.println(k + "=" + v));
//English=[Country:UK, Country:US]
//French=[Country:France, Country:Canada]
}
static class Country {
String name;
List<String> languages;
public Country(String name, List<String> languages) {
this.name = name;
this.languages = languages;
}
public List<String> getLanguages() {
return languages;
}
@Override
public String toString() {
return "Country:" + name;
}
}
假设我有一个国家/地区对象列表,其中包含该国家/地区使用的语言列表,如下所示:
class Country {
List<String> languages;
}
我想创建以下格式的地图:
Map<String, List<Country>>
,这样每种语言都映射到国家/地区对象列表。例如:
"French" -> [Country:France, Country:Canada],
"English" -> [Country:UK, Country:US]
性能是这里的一个问题,所以我想避免多次迭代和查找。我试过使用 groupingBy
,但如何 flatMap
键集?
例如,结果为 Map<List<String>, List<Country>>
:
countries.stream()
.collect(Collectors.groupingBy(country -> country.getLanguages(), toList()));
这样就可以了:
countries.stream()
.flatMap(country -> country.getLanguages()
.stream()
.map(lang -> new SimpleEntry<>(lang,
new ArrayList<>(Arrays.asList(country)))))
.collect(Collectors.toMap(
Entry::getKey,
Entry::getValue,
(l1, l2) -> {
l1.addAll(l2);
return l2;
}));
由于您似乎很在意性能,所以不要将流用于这个简单的任务:
Map<String, List<Country>> countriesByLanguage = new HashMap<>();
for (Country country : countries) {
for (String language : country.getLanguages()) {
countriesByLanguage.computeIfAbsent(language, k -> new ArrayList<>())
.add(country);
}
}
您可以使用 流中的流 来实现,如下所示:首先遍历国家列表,然后遍历嵌套的语言列表并准备 «language, country»
对,然后收集起来映射:
public static void main(String[] args) {
List<Country> countries = List.of(
new Country("France", List.of("French")),
new Country("Canada", List.of("French")),
new Country("UK", List.of("English")),
new Country("US", List.of("English")));
Map<String, List<Country>> map = countries.stream()
// Stream<Map.Entry<String,Country>>
.flatMap(country -> country.getLanguages().stream()
.map(lang -> Map.entry(lang, country)))
.collect(Collectors.toMap(
// key - language
Map.Entry::getKey,
// value - List<Country>
entry -> new ArrayList<>(List.of(entry.getValue())),
// merge duplicates, if any
(list1, list2) -> {
list1.addAll(list2);
return list1;
}
));
// output
map.forEach((k, v) -> System.out.println(k + "=" + v));
//English=[Country:UK, Country:US]
//French=[Country:France, Country:Canada]
}
static class Country {
String name;
List<String> languages;
public Country(String name, List<String> languages) {
this.name = name;
this.languages = languages;
}
public List<String> getLanguages() {
return languages;
}
@Override
public String toString() {
return "Country:" + name;
}
}