将hashmap拆分为java 8中的分区

时间:2015-12-21 22:31:44

标签: java collections hashmap java-8

我有hashmap:Map<String, Set<String>> myMap

我希望将其拆分为包含Map的列表:

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

,每张地图最多100个。 我知道如何以常规的方式做到这一点..(关于入口集的foreach,每100个项目创建新的地图)。 有没有选择用java 8 lambda做什么? (像Lists.partitions() ..)?

2 个答案:

答案 0 :(得分:4)

使用this中的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 :(得分:2)

将流拆分为有序的固定大小的块(如在Lists.partition中)是不可能的,因为在并行执行中,每个块必须等待其左空间块被完全处理。

但是,如果您不关心生成的子映射中的键的顺序(因为它将由Map#iterator的方法返回),那么您可以滚动自定义收集器。 / p>

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