我有一张我想填充的地图:
private Map<String, Set<String>> myMap = new HashMap<>();
用这种方法:
private void compute(String key, String[] parts) {
myMap.computeIfAbsent(key, k -> getMessage(parts));
}
compute()
的调用如下:
for (String line : messages) {
String[] parts = line.split("-");
validator.validate(parts); //validates parts are as expected
String key = parts[parts.length - 1];
compute(key, parts);
}
parts
元素是这样的:
[AB, CC, 123]
[AB, FF, 123]
[AB, 456]
在compute()
方法中,您可以看到我尝试将数组元素的最后一部分用作key
,其他部分用作values
为了我想要建立的地图。
我的问题:如何使用Java 8功能样式仅向现有密钥添加唯一值,例如
{123=[AB, FF, CC]}
答案 0 :(得分:1)
根据您的要求,我添加了一个lambda变体,它只是通过lambda将部件添加到compute
- 方法中的地图:
private void compute(String key, String[] parts) {
myMap.computeIfAbsent(key,
s -> Stream.of(parts)
.limit(parts.length - 1)
.collect(toSet()));
}
但在这种情况下,您只能在地图中获得123=[AB, CC]
之类的内容。如果您还要添加后续调用中的所有值,请使用merge
:
private void compute(String key, String[] parts) {
myMap.merge(key,
s -> Stream.of(parts)
.limit(parts.length - 1)
.collect(toSet()),
(currentSet, newSet) -> {currentSet.addAll(newSet); return currentSet;});
}
我不确定您对computeIfAbsent
的意图,但是从您列为parts
的内容以及您对输出的期望,您可能还想尝试以下内容而不是您列出的整个代码:
// the function to identify your key
Function<String[], String> keyFunction = strings -> strings[strings.length - 1];
// the function to identify your values
Function<String[], List<String>> valuesFunction = strings -> Arrays.asList(strings).subList(0, strings.length - 1);
// a collector to add all entries of a collection to a (sorted) TreeSet
Collector<List<String>, TreeSet<Object>, TreeSet<Object>> listTreeSetCollector = Collector.of(TreeSet::new, TreeSet::addAll, (left, right) -> {
left.addAll(right);
return left;
});
Map myMap = Arrays.stream(messages) // or: messages.stream()
.map(s -> s.split("-"))
.peek(validator::validate)
.collect(Collectors.groupingBy(keyFunction,
Collectors.mapping(valuesFunction, listTreeSetCollector)));
使用您的样本作为输入,您将获得您提到的结果(实际上,实际排序,因为我使用了TreeSet)。
String[] messages = new String[]{
"AB-CC-123",
"AB-FF-123",
"AB-456"};
生成一张包含以下内容的地图:
123=[AB, CC, FF]
456=[AB]
最后但并非最不重要:如果可以,请将密钥和值本身传递给您的方法。不要分割关于识别密钥和识别值的逻辑。这使得以后或其他人很难理解您的代码。
答案 1 :(得分:1)
要向可能存在的键添加更多部分,您使用了错误的方法;你想要merge()
,而不是computeIfAbsent()
。
如果validator.valudate()
抛出已检查的异常,则必须在流外调用它,因此您需要一个foreach循环:
for (String message : messages) {
String[] parts = message.split("-");
validator.validate(parts);
LinkedList<String> list = new LinkedList(Arrays.asList(parts));
String key = list.getLast();
list.removeLast();
myMap.merge(key, new HashSet<>(list), Set::addAll);
}
使用LinkedList
,其方法为getLast()
和removeLast()
,可使代码具有可读性。
免责声明:代码可能无法编译或工作,因为它在我的手机上被翻阅(但它有可能起作用)
答案 2 :(得分:1)
试试这个:
private void compute(String[] parts) {
int lastIndex = parts.length - 1;
String key = parts[lastIndex];
List<String> values = Arrays.asList(parts).subList(0, lastIndex);
myMap.computeIfAbsent(key, k -> new HashSet<>()).addAll(values);
}
或者,如果您愿意,可以用流替换整个循环:
Map<String, Set<String>> myMap = messages.stream() // if messages is an array, use Arrays.stream(messages)
.map(line -> line.split("-"))
.peek(validator::validate)
.collect(Collectors.toMap(
parts -> parts[parts.length - 1],
parts -> new HashSet<>(Arrays.asList(parts).subList(0, parts.length - 1)),
(a, b) -> { a.addAll(b); return a; }));