将非重复元素添加到java 8函数样式的现有键中

时间:2016-06-30 12:49:02

标签: java lambda hashmap

我有一张我想填充的地图:

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]}

3 个答案:

答案 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; }));