使用Java8 Stream在HashMap中翻转键值

时间:2017-08-24 22:09:45

标签: java-8 java-stream

我在以下结构中有Map,我想翻转键和值。

Map<String, List<String>> dataMap

示例数据:

acct01: [aa, ab, ad],
acct02: [ac, ad]
acct03: [ax, ab]

希望将此数据转换为

aa: [acct01],
ab: [acct01, acct03],
ac: [acct02],
ad: [acct01, acct02],
ax: [acct03]

想知道是否有一种java 8 - 流方式来转换Map。

我当前的实施(没有流)

Map<String, List<String>> originalData = new HashMap<String, List<String>>();
        originalData.put("Acct01", Arrays.asList("aa", "ab", "ad"));
        originalData.put("Acct02", Arrays.asList("ac", "ad"));
        originalData.put("Acct03", Arrays.asList("ax", "ab"));

        System.out.println(originalData);
        Map<String, List<String>> newData = new HashMap<String, List<String>>();
        originalData.entrySet().forEach(entry -> {
            entry.getValue().forEach(v -> {
                if(newData.get(v) == null) {
                    List<String> t = new ArrayList<String>();
                    t.add(entry.getKey());
                    newData.put(v, t);
                } else {
                    newData.get(v).add(entry.getKey());
                }
            });
        });
        System.out.println(newData);

输入和输出, {Acct01=[aa, ab, ad], Acct02=[ac, ad], Acct03=[ax, ab]} {aa=[Acct01], ab=[Acct01, Acct03], ac=[Acct02], ad=[Acct01, Acct02], ax=[Acct03]}

寻找使用Stream实现的方法。

4 个答案:

答案 0 :(得分:3)

获取条目集的流,将其展平为每个键值对的一个条目,按值分组,将相关的密钥收集到列表中。

import static java.util.Arrays.asList;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;

import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

<K, V> Map<V, List<K>> invert(Map<K, List<V>> map) {
    return map.entrySet()
              .stream()
              .flatMap(entry ->
                  entry.getValue()
                       .stream()
                       .map(value ->
                           new SimpleImmutableEntry<>(
                               entry.getKey(),
                               value
                           )
                       )
              )
              .collect(
                  groupingBy(
                      Entry::getValue,
                      mapping(
                          Entry::getKey,
                          toList()
                      )
                  )
              );
}

答案 1 :(得分:2)

以下是Java 8流库的解决方案:StreamEx

newData = EntryStream.of(originalData).invert().flatMapKeys(k -> k.stream()).grouping();

答案 2 :(得分:2)

如果您愿意使用Eclipse Collections之类的第三方库,则可以使用ListMultimap(每个密钥可以包含List个值。 Multimap有flip()。所以,这将有效:

MutableListMultimap<String, String> originalData = Multimaps.mutable.list.empty();
originalData.putAll("Acct01", Arrays.asList("aa", "ab", "ad"));
originalData.putAll("Acct02", Arrays.asList("ac", "ad"));
originalData.putAll("Acct03", Arrays.asList("ax", "ab"));
System.out.println(originalData);

MutableBagMultimap<String, String> newData = originalData.flip();
System.out.println(newData);

输入:{Acct03=[ax, ab], Acct02=[ac, ad], Acct01=[aa, ab, ad]}

输出:{ac=[Acct02], ad=[Acct02, Acct01], aa=[Acct01], ab=[Acct03, Acct01], ax=[Acct03]}

请注意,flip()会返回BagMultimap,其中每个密钥都可以包含Bag个值。 Bag是特殊的数据结构,它是无序的并允许重复。

注意:我是Eclipse Collections的提交者。

答案 3 :(得分:0)

您当前的实现已经依赖于Java8的功能。 {J}中的forEach方法被添加到了许多数据结构中,虽然您可以使用流,但没有必要,因为流的许多优点来自于能够执行过滤,排序和其他懒惰方式的方法,不适用于密钥重映射。

如果您真的想要,可以在示例中将所有.forEach个实例更改为.stream().forEach,从而将其分成几个流。