簡體   English   中英

展平地圖<Integer, List<String> &gt; 到地圖<String, Integer>使用流和 lambda

[英]Flatten a Map<Integer, List<String>> to Map<String, Integer> with stream and lambda

我想展平一個Map ,它將一個Integer鍵關聯到一個String列表,而不會丟失鍵映射。 我很好奇,好像用streamlambda這樣做是可能和有用的。

我們從這樣的事情開始:

Map<Integer, List<String>> mapFrom = new HashMap<>();

讓我們假設 mapFrom 填充在某處,看起來像:

1: a,b,c
2: d,e,f
etc.

我們還假設列表中的值是唯一的。

現在,我想“展開”它以獲得第二張地圖,例如:

a: 1
b: 1
c: 1
d: 2
e: 2
f: 2
etc.

我可以這樣做(或者非常相似,使用foreach ):

Map<String, Integer> mapTo = new HashMap<>();
for (Map.Entry<Integer, List<String>> entry: mapFrom.entrySet()) {
    for (String s: entry.getValue()) {
        mapTo.put(s, entry.getKey());
    }
}

現在讓我們假設我想使用 lambda 而不是嵌套的for循環。 我可能會做這樣的事情:

Map<String, Integer> mapTo = mapFrom.entrySet().stream().map(e -> {
    e.getValue().stream().?
    // Here I can iterate on each List, 
    // but my best try would only give me a flat map for each key, 
    // that I wouldn't know how to flatten.
}).collect(Collectors.toMap(/*A String value*/,/*An Integer key*/))

我也嘗試過flatMap ,但我不認為這是正確的方法,因為雖然它幫助我擺脫了維度問題,但我在這個過程中失去了關鍵。

簡而言之,我的兩個問題是:

  • 是否可以使用streamslambda來實現這一點?
  • 這樣做有用嗎(性能、可讀性)?

您需要使用flatMap將值展平為新流,但由於您仍需要用於收集到Map的原始鍵,因此必須映射到包含鍵和值的臨時對象,例如

Map<String, Integer> mapTo = mapFrom.entrySet().stream()
       .flatMap(e->e.getValue().stream()
                    .map(v->new AbstractMap.SimpleImmutableEntry<>(e.getKey(), v)))
       .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

Map.Entry是不存在的元組類型的替代,任何其他能夠容納兩個不同類型的對象的類型就足夠了。

另一種不需要這些臨時對象的方法是自定義收集器:

Map<String, Integer> mapTo = mapFrom.entrySet().stream().collect(
    HashMap::new, (m,e)->e.getValue().forEach(v->m.put(v, e.getKey())), Map::putAll);

這與toMap在靜默覆蓋重復鍵時不同,而如果存在重復鍵,沒有合並函數的toMap將拋出異常。 基本上,這個自定義收集器是一個並行能力的變種

Map<String, Integer> mapTo = new HashMap<>();
mapFrom.forEach((k, l) -> l.forEach(v -> mapTo.put(v, k)));

但請注意,即使使用非常大的輸入映射,此任務也不會受益於並行處理。 只有在流管道中存在可以從SMP中受益的額外計算密集任務時,才有可能從並行流中獲益。 或許,簡潔,順序的Collection API解決方案更可取。

你應該使用flatMap如下:

entrySet.stream()
        .flatMap(e -> e.getValue().stream()
                       .map(s -> new SimpleImmutableEntry(e.getKey(), s)));

SimpleImmutableEntryAbstractMap的嵌套類。

希望這會以最簡單的方式完成。 :))

mapFrom.forEach((key, values) -> values.forEach(value -> mapTo.put(value, key)));

這應該工作。 請注意,您從列表中丟失了一些密鑰。

Map<Integer, List<String>> mapFrom = new HashMap<>();
Map<String, Integer> mapTo = mapFrom.entrySet().stream()
        .flatMap(integerListEntry -> integerListEntry.getValue()
                .stream()
                .map(listItem -> new AbstractMap.SimpleEntry<>(listItem, integerListEntry.getKey())))
        .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));

與之前的 Java 9 答案相同:

Map<String, Integer> mapTo = mapFrom.entrySet()
        .stream()
        .flatMap(entry -> entry.getValue()
                .stream()
                .map(s -> Map.entry(s, entry.getKey())))
        .collect(toMap(Entry::getKey, Entry::getValue));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM