bbK*_*ing 2 java hashmap java-stream groupingby
我正在尝试做的事情的简化示例:
假设我有一个字符串列表,需要根据是否包含特定子字符串的条件将其分为 4 组。如果一个字符串包含Foo
它应该属于该组FOO
,如果它包含Bar
它应该属于该组BAR
,如果它包含两者它应该出现在两个组中。
List<String> strings = List.of("Foo", "FooBar", "FooBarBaz", "XXX");
Run Code Online (Sandbox Code Playgroud)
由于字符串被分组到第一个匹配组中,因此上述输入的简单方法无法按预期工作:
Map<String,List<String>> result1 =
strings.stream()
.collect(Collectors.groupingBy(
str -> str.contains("Foo") ? "FOO" :
str.contains("Bar") ? "BAR" :
str.contains("Baz") ? "BAZ" : "DEFAULT"));
Run Code Online (Sandbox Code Playgroud)
结果1是
{FOO=[Foo, FooBar, FooBarBaz], DEFAULT=[XXX]}
Run Code Online (Sandbox Code Playgroud)
期望的结果应该是
{FOO=[Foo, FooBar, FooBarBaz], BAR=[FooBar, FooBarBaz], BAZ=[FooBarBaz], DEFAULT=[XXX]}
Run Code Online (Sandbox Code Playgroud)
经过一段时间的搜索,我找到了另一种方法,它接近我想要的结果,但不太完全
Map<String,List<String>> result2 =
List.of("Foo", "Bar", "Baz", "Default").stream()
.flatMap(str -> strings.stream().filter(s -> s.contains(str)).map(s -> new String[]{str.toUpperCase(), s}))
.collect(Collectors.groupingBy(arr -> arr[0], Collectors.mapping(arr -> arr[1], Collectors.toList())));
System.out.println(result2);
Run Code Online (Sandbox Code Playgroud)
结果2是
{BAR=[FooBar, FooBarBaz], FOO=[Foo, FooBar, FooBarBaz], BAZ=[FooBarBaz]}
Run Code Online (Sandbox Code Playgroud)
虽然这可以正确地将包含子字符串的字符串分组到所需的组中,但不包含子字符串并因此应属于默认组的字符串将被忽略。期望的结果如上所述(顺序无关紧要)
{BAR=[FooBar, FooBarBaz], FOO=[Foo, FooBar, FooBarBaz], BAZ=[FooBarBaz], DEFAULT=[XXX]}
Run Code Online (Sandbox Code Playgroud)
现在我正在使用结果图并做额外的事情:
result2.put("DEFAULT", result1.get("DEFAULT"));
Run Code Online (Sandbox Code Playgroud)
以上这些可以一步完成吗?有没有比我上面的方法更好的方法?
与其使用字符串、"Foo"
等"Bar"
及其相应的大写版本进行操作,不如定义一个.enum
我们称它为Keys
:
public enum Keys {
FOO("Foo"), BAR("Bar"), BAZ("Baz"), DEFAULT("");
private static final Set<Keys> nonDefaultKeys = EnumSet.range(FOO, BAZ); // Set of enum constants (not includes DEFAULT), needed to avoid creating EnumSet or array of constants via `values()` at every invocation of getKeys()
private String keyName;
Keys(String keyName) {
this.keyName = keyName;
}
public static List<String> getKeys(String str) {
List<String> keys = nonDefaultKeys.stream()
.filter(key -> str.contains(key.keyName))
.map(Enum::name)
.toList();
// if non-default keys not found, i.e. keys.isEmpty() - return the DEFAULT
return keys.isEmpty() ? List.of(DEFAULT.name()) : keys;
}
}
Run Code Online (Sandbox Code Playgroud)
它有一个方法getKeys(String)
需要一个字符串并返回给 定字符串应映射到的键列表。
通过使用封装在Keys
枚举中的功能,我们可以创建一个字符串映射,该映射被分成与常量Keys
名称相对应的组。collect(supplier,accumulator,combiner)
main()
public static void main(String[] args) {
List<String> strings = List.of("Foo", "FooBar", "FooBarBaz", "XXX");
Map<String, List<String>> stringsByGroup = strings.stream()
.collect(
HashMap::new, // mutable container - which will contain results of mutable reduction
(Map<String, List<String>> map, String next) -> Keys.getKeys(next)
.forEach(key -> map.computeIfAbsent(key, k -> new ArrayList<>()).add(next)), // accumulator function - defines how to store stream elements into the container
(left, right) -> right.forEach((k, v) ->
left.merge(k, v, (oldV, newV) -> { oldV.addAll(newV); return oldV; }) // combiner function - defines how to merge container while executing the stream in parallel
));
stringsByGroup.forEach((k, v) -> System.out.println(k + " -> " + v));
}
Run Code Online (Sandbox Code Playgroud)
输出:
BAR -> [FooBar, FooBarBaz]
FOO -> [Foo, FooBar, FooBarBaz]
BAZ -> [FooBarBaz]
DEFAULT -> [XXX]
Run Code Online (Sandbox Code Playgroud)
这是使用mapMulti 的理想选择。 MapMulti 采用流值的BiConsumer和Consumer。消费者习惯于简单地将某些东西放回流中。之所以将其添加到 Java 中是因为可能会产生不需要的开销。flatMaps
这是通过构建一个字符串数组来实现的,就像您之前对Token和包含字符串所做的那样并收集(也像您之前所做的那样)。如果在字符串中找到键,则接受包含该键和包含字符串的字符串数组。否则,接受带有默认键和字符串的字符串数组。
List<String> strings =
List.of("Foo", "FooBar", "FooBarBaz", "XXX", "YYY");
Map<String, List<String>> result = strings.stream()
.<String[]>mapMulti((str, consumer) -> {
boolean found = false;
String temp = str.toUpperCase();
for (String token : List.of("FOO", "BAR",
"BAZ")) {
if (temp.contains(token)) {
consumer.accept(
new String[] { token, str });
found = true;
}
}
if (!found) {
consumer.accept(
new String[] { "DEFAULT", str });
}
})
.collect(Collectors.groupingBy(arr -> arr[0],
Collectors.mapping(arr -> arr[1],
Collectors.toList())));
result.entrySet().forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
印刷
BAR=[FooBar, FooBarBaz]
FOO=[Foo, FooBar, FooBarBaz]
BAZ=[FooBarBaz]
DEFAULT=[XXX, YYY]
Run Code Online (Sandbox Code Playgroud)
请记住,流旨在让您的编码世界变得更轻松。但有时,只需要使用一些 Java 8 构造的常规循环即可。除了学术练习之外,我可能会像这样完成任务。
Map<String,List<String>> result2 = new HashMap<>();
for (String str : strings) {
boolean added = false;
String temp = str.toUpperCase();
for (String token : List.of("FOO","BAR","BAZ")) {
if(temp.contains(token)) {
result2.computeIfAbsent(token, v->new ArrayList<>()).add(str);
added = true;
}
}
if (!added) {
result2.computeIfAbsent("DEFAULT", v-> new ArrayList<>()).add(str);
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
613 次 |
最近记录: |