2016-08-15 42 views
-3

我期待轉換Collection<Placement>Map<String, Collection<String>>使用Java流,其中Placement類是如下轉換集合地圖<字符串,集合<String>>使用Java 8流

public class Placement { 

    private String id; 

    /* Links two placements, may be same for atleast 2 placements. */ 
    private String futureLinkId; 
} 

爲地圖的關鍵是未來的鏈接ID,值是placement ID。例如,如果有2個展示位置 p1 { id=1, futureLinkId=f1 }, p2 { id=2, futureLinkId=f1 },輸出應該是Map { f1 - { id1, id2 } }

+0

什麼'地圖{F1 - {ID1,ID2}}'是什麼意思? Java中的'Map'需要有一個鍵類型,這裏是'String',和一個值類型,你不會告訴我們。 – ajb

+0

無論如何,解決方案可能會涉及使用[此方法](http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toMap-java.util。 function.Function-java.util.function.Function-java.util.function.BinaryOperator-)。 – ajb

+0

數據類型字符串@ajb f1是Map的關鍵字,值爲字符串集合是具有相同未來鏈接ID的展示位置的ID,在本示例中爲f1。 – San82

回答

2

這其實很簡單,儘管其他人提供的令人費解的解決方案:

Map<String, List<String>> map = placements.stream() 
     .collect(groupingBy(Placement::getFutureLinkId, mapping(Placement::getId, toList()))); 
+0

我猜想其他答案儘管來自可能並不那麼愚蠢的人,但其答案仍然「令人費解」。原因在於Java收集器是愚蠢的無法發現的,因爲它們與通常的FP樣式收集API截然不同。我第一次看到這個「映射」的東西,很高興知道。我發現他們根本不靈活。通常你會希望調用頂層的所有方法,但是在這個API中,'collect'可以變得過於複雜並且包含太多的邏輯 – Dici

0

可以將值簡單地按futureLinkId然後映射到id。一種方式把它寫:

Map<String, List<String>> result = Stream.of(new Placement("id1", "f1"), new Placement("id2", "f1")) 
     .collect(Collectors.groupingBy(Placement::getFutureLinkId)) 
     .entrySet() 
     .stream() 
     .collect(Collectors.toMap(
       Map.Entry::getKey, 
       entry -> entry.getValue().stream().map(Placement::getId).collect(Collectors.toList()))); 
System.out.println(result); 

輸出:

{f1=[id1, id2]} 

爲了使它看起來更好,我建議寫兩個幫手:

public static <K, V, N> Map<K, N> mapValues(Map<K, V> map, Function<V, N> mapper) { 
    return map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, mapper.compose(Map.Entry::getValue))); 
} 

public static <T, R> List<R> map(List<T> list, Function<T, R> mapper) { 
    return list.stream().map(mapper).collect(toList()); 
} 

然後

Map<String, List<String>> result = Stream.of(new Placement("id1", "f1"), new Placement("id2", "f1")).collect(
    collectingAndThen(groupingBy(Placement::getFutureLinkId), 
         res -> mapValues(res, list -> map(list, Placement::getId)))); 

System.out.println(result); 
+0

這個答案絕對正確,所以如果你想downvote它首先添加評論:) – Dici

+0

不是downvoter,但正確性不是唯一的原因可能downvote答案。 – shmosel

+0

@shmosel這正是爲什麼要評論才能降低正確答案的原因。如果答案顯然是錯誤的,沒有人會責怪downvoter沒有留下評論,但是當它是正確的時候......它不是建設性的只是downvote – Dici

0

使用three-argument toMap是滿足的一種方式找出你需要的東西。 (groupingBy似乎是另一種,但我不熟悉這個方法)

toMap,你需要你的Placement一鍵映射功能;一個函數,它將您的Placement映射爲一個值,在這種情況下,該值將需要爲集合的值;最後一個函數會將兩個值合併在一起,如果它們具有相同的密鑰。喜歡的東西

Map<String,HashSet<String>> result = 
    placements.stream() 
     .collect(Collectors.toMap 
      (placement -> placement.futureLinkId, 
       placement -> new HashSet(Collections.singleton(placement.id)), 
       (s1, s2) -> { HashSet<String> result = new HashSet<>(s1); 
          result.add(s2); 
          return result; })); 

注:我沒有測試過這一點。如果流是順序流,則可能無需複製s1即可運行。

+1

Java收藏家是如此糟糕,我認爲這是一個完美的例子。你的代碼比我的簡單,而我正在使用爲此目的而製作的方法。但是,您確實創建了很多對象。構造可變單例的助手會使這個更好。他們應該採用Scala收集API的示例,這是更加強大和美觀的(但是,很多是由於Scala特有的功能) – Dici

+0

使用placements.stream() .collect(Collectors.groupingBy(placement。 ID,Collectors.toSet())); – user1846749

相關問題