2015-12-21 156 views
1

分區我的HashMap:Map<String, Set<String>> myMap拆分HashMap來在Java 8

,我想分裂它列出包含Map

List<Map<String,Set<String>>> listofMaps; 

,每個地圖將是最大100個鍵。 我知道如何以正規的方式做到這一點..(foreach在入口集,每100個項目創建新的地圖)。 是否有任何選項可以用java 8 lambda或其他東西來做到這一點? (類似Lists.partitions() ..)?

+2

沒有什麼特別。這不是那種容易(甚至根本不能)用Java 8流表達的操作。 –

+1

如果你指的是番石榴的'Lists.partition',這是一個可行的方法,可能與Java 8結合使用,儘管你可以使用'Iterables.partition'來代替。 –

回答

3

用我unorderedBatches()收集來自this答案:

Collector<Entry<String, Set<String>>, ?, List<Map<String, Set<String>>>> batchesCollector = 
    unorderedBatches(100, 
     Collectors.toMap(Entry::getKey, Entry::getValue), Collectors.toList()); 
List<Map<String, Set<String>>> listofMaps = myMap.entrySet().stream() 
     .collect(batchesCollector); 
+0

大大更多的可重複使用比我下車,好的工作..再次:-)。 –

1

將流拆分爲有序固定大小的塊(如Lists.partition)是不可能的,因爲在並行執行中,每個塊將不得不等待其左空間塊被完全處理。

如果您不關心結果子地圖中按鍵的順序(因爲它將由Map#iterator的方法返回),那麼您可以滾動自定義收集器。

private static <K, V> Collector<Map.Entry<K, V>, ?, List<Map<K, V>>> mapSize(int limit) { 
    return Collector.of(ArrayList::new, 
      (l, e) -> { 
       if (l.isEmpty() || l.get(l.size() - 1).size() == limit) { 
        l.add(new HashMap<>()); 
       } 
       l.get(l.size() - 1).put(e.getKey(), e.getValue()); 
      }, 
      (l1, l2) -> { 
       if (l1.isEmpty()) { 
        return l2; 
       } 
       if (l2.isEmpty()) { 
        return l1; 
       } 
       if (l1.get(l1.size() - 1).size() < limit) { 
        Map<K, V> map = l1.get(l1.size() - 1); 
        ListIterator<Map<K, V>> mapsIte = l2.listIterator(l2.size()); 
        while (mapsIte.hasPrevious() && map.size() < limit) { 
         Iterator<Map.Entry<K, V>> ite = mapsIte.previous().entrySet().iterator(); 
         while (ite.hasNext() && map.size() < limit) { 
          Map.Entry<K, V> entry = ite.next(); 
          map.put(entry.getKey(), entry.getValue()); 
          ite.remove(); 
         } 
         if (!ite.hasNext()) { 
          mapsIte.remove(); 
         } 
        } 
       } 
       l1.addAll(l2); 
       return l1; 
      } 
    ); 
} 

這一個將地圖項作爲值並將它們放入List<Map<K,V>>

累加器,檢查當前列表是否爲空或最後一個地圖的大小是否達到限制。如果是這樣的話,它會添加一個新的地圖。 然後,將當前所處理條目的新映射添加到地圖中。

組合器需要組合兩個並行構建的列表。如果其中一個列表爲空,則返回另一個列表。如果不是這種情況,則需要檢查第一個列表的最後一個地圖是否具有所需元素的數量。如果不是這種情況,我們抓住第二個列表的最後一個地圖,並且將元素添加到第一個列表的最後一個地圖。如果達到限制或者第二個列表中沒有更多要添加的元素,則停止。如果所有元素都被消耗掉,請不要忘記刪除空白地圖。

一種這樣的集電極的用法是:

List<Map<String, Set<String>>> listofMaps = 
       myMap.entrySet().stream().collect(mapSize(2)); 

一些實例(與兩個並行和串行流)與由13個鍵 - 值映射的初始地圖:

Size of maps 2 
{11=[11a, 11b], 12=[12a, 12b]} 
{13=[13b, 13a], 8=[8a, 8b]} 
{1=[1a, 1b], 2=[2b, 2a]} 
{3=[3a, 3b], 6=[6a, 6b]} 
{4=[4a, 4b], 5=[5a, 5b]} 
{7=[7a, 7b], 10=[10a, 10b]} 
{9=[9a, 9b]} 
============================= 
Size of maps 5 
{11=[11a, 11b], 12=[12a, 12b], 13=[13b, 13a], 6=[6a, 6b], 7=[7a, 7b]} 
{1=[1a, 1b], 2=[2b, 2a], 3=[3a, 3b], 4=[4a, 4b], 5=[5a, 5b]} 
{8=[8a, 8b], 9=[9a, 9b], 10=[10a, 10b]} 
============================= 
Size of maps 12 
{11=[11a, 11b], 12=[12a, 12b], 1=[1a, 1b], 13=[13b, 13a], 2=[2b, 2a], 3=[3a, 3b], 4=[4a, 4b], 5=[5a, 5b], 6=[6a, 6b], 7=[7a, 7b], 8=[8a, 8b], 9=[9a, 9b]} 
{10=[10a, 10b]} 
============================= 
Size of maps 15 
{11=[11a, 11b], 12=[12a, 12b], 13=[13b, 13a], 1=[1a, 1b], 2=[2b, 2a], 3=[3a, 3b], 4=[4a, 4b], 5=[5a, 5b], 6=[6a, 6b], 7=[7a, 7b], 8=[8a, 8b], 9=[9a, 9b], 10=[10a, 10b]} 

我沒有廣泛測試它。另外我認爲你可以修改它,使它更通用。

例如,您可能會接受任意對象,並使用兩個函數爲您正在處理的每個實例生成一個鍵和值。

private static <T, K, V> Collector<T, ?, List<Map<K, V>>> mapSize(Function<T, K> keyFunc, Function<T, V> mapFunc, int limit) { 

l.get(l.size() - 1).put(keyFunc.apply(e), mapFunc.apply(e)); 

,並調用它像:

.collect(mapSize(Map.Entry::getKey, Map.Entry::getValue, size));