在Java 8中将哈希映射拆分为分区

5

我有一个哈希映射:Map<String, Set<String>> myMap

我想将它拆分成包含Map的列表:

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

每个map最多只能有100个key。

我知道如何用常规方法实现这个功能..(对entrySet进行foreach,每100个项目创建新的map)。

是否有使用java 8 lambda或其他方法来实现的选项?(类似于Lists.partitions()..)?


2
并不是特别适合使用Java 8流来表达的操作。 - Louis Wasserman
1
如果你在提到Guava的Lists.partition,那是一个可行的方法,可能结合Java 8一起使用,不过你也可以使用Iterables.partition - Louis Wasserman
2个回答

7

使用我从这个答案中提供的unorderedBatches()收集器:

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);

1
比我之前使用的更可重用,干得好..再次感谢 :-)。 - Alexis C.
@Tagir Valeev,当我使用unorderedlist方法时出现了一些错误。您能帮忙解决一下吗? - Sachin HR

2

将流分成有序的固定大小块(例如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;
            }
    );
}

这个方法将map条目作为值,放入一个List<Map<K,V>>中。

累加器会检查当前列表是否为空或者上一个map的大小是否已达到限制。如果是这种情况,就会添加一个新的map。然后将当前处理的条目的新映射添加到map中。

组合器需要组合并行构建的两个列表。如果其中一个列表为空,则返回另一个列表。如果不是这种情况,您需要检查第一个列表的最后一个map是否具有所需数量的元素。如果不是这种情况,我们获取第二个列表的最后一个map,并将元素添加到第一个列表的最后一个map中。如果达到限制或者没有更多来自第二个列表的元素可以添加,则停止。请不要忘记在所有元素都被消耗完毕时删除空map。

这样一个收集器的用途之一是:

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));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接