将一个Hashmap分成两个较小的Map

6
我有一个 key 为 K,value 为 V 的 hashmap,我想将其拆分为两个 subMap。

HashMap<Long,JSONObject>

其中一种方法是使用 TreeMap,并进行子映射。
TreeMap<Integer, Integer> sorted = new TreeMap<Integer, Integer>(bigMap);

SortedMap<Integer, Integer> zeroToFortyNine = sorted.subMap(0, 50);
SortedMap<Integer, Integer> fiftyToNinetyNine = sorted.subMap(50, 100);

但问题是我没有从jsonObject中获取subMap,而我只想用HashMap实现它。
谢谢。

什么是拆分 HashMap 的标准?由于它没有顺序,所以将其一分为二并不容易,因为“一半”未被定义。 - Lino
1
使用 sorted.entrySet() 怎么样?遍历 HashMap 并将条目放入另外两个映射中。 - user1802604
6个回答

6

您可以利用 Java 8 流式 API

Map<Long, JSONObject> map = ...;
AtomicInteger counter = new AtomicInteger(0);
Map<Boolean, Map<Long, JSONObject>> collect = map.entrySet()
    .stream()
   .collect(Collectors.partitioningBy(
       e -> counter.getAndIncrement() < map.size() / 2, // this splits the map into 2 parts
       Collectors.toMap(
           Map.Entry::getKey, 
           Map.Entry::getValue
       )
   ));

这将地图分成两半,第一部分(map.get(true))包含所有从中间以下的元素,第二部分(map.get(false))包含所有从中间及以上的元素。


除非键值从1到map.size,否则如果是随机值,则无法将其分割为“等效”大小的映射。 - AxelH
@AxelH 谢谢提及。我已经编辑了代码,使用递增计数器代替了。 - Lino
我原本没有想到要使用原子计数器,顺便说一下,你可以使用整数计数器,map.size 无法容纳 long - AxelH
@AxelH,嗯,在您的回答中,您将地图均匀分配,而我只是将前半部分放入地图中,然后将另一半放入另一个地图中 :) - Lino
1
我同意,但我不确定他是否收到了一个排序过的映射,因为“_我只想用HashMap做它。_”所以对我来说,顺序并不重要。最终两种解决方案都是有效的,一种是使用Stream,另一种是使用迭代器。完美 ;) - AxelH

2
如果您无法使用值或键来确定分割位置,可以通过计算迭代次数来实现:
Map<Long, String> map = new HashMap<>();
Map<Long, String> sub1 = new HashMap<>();
Map<Long, String> sub2 = new HashMap<>();

int i = 0;
for(Map.Entry<Long, String> e : map.entrySet()){
    (i++ % 2 == 0 ? sub1:sub2).put(e.getKey(), e.getValue());
}

我使用了三目运算符来增加计数器并选择地图。因此它将在这两个地图上平均分配值。

测试:

    Map<Long, String> map = new HashMap<>();
    map.put(1L, "foo");
    map.put(2L, "bar");
    map.put(3L, "for");
    map.put(4L, "far");

    Map<Long, String> sub1 = new HashMap<>();
    Map<Long, String> sub2 = new HashMap<>();

    int i = 0;
    for(Map.Entry<Long, String> e : map.entrySet()){
        (i++ % 2 == 0 ? sub1:sub2).put(e.getKey(), e.getValue());
    }

    System.out.println(sub1);
    System.out.println(sub2);

{1=foo, 3=for}
{2=bar, 4=far}

如果需要,这将很容易适应分割为3、4或任意数量的地图:

public static void main(String[] args) {
    Map<Long, String> map = new HashMap<>();
    map.put(1L, "foo");
    map.put(2L, "bar");
    map.put(3L, "for");
    map.put(4L, "far");

    Map<Long, String> sub1 = new HashMap<>();
    Map<Long, String> sub2 = new HashMap<>();
    Map<Long, String> sub3 = new HashMap<>();

    split(map, sub1, sub2, sub3);

    System.out.println(sub1);
    System.out.println(sub2);
    System.out.println(sub3);
}

@SafeVarargs
public static <T, U> void split(Map<T,U> map, Map<T,U>... array){
    int i = 0;
    for(Map.Entry<T, U> e : map.entrySet()){
        array[i++% array.length].put(e.getKey(), e.getValue());
    }
}

{1=foo, 4=far}
{2=bar}
{3=for}


1
你可以循环遍历条目集并填充两个不同的映射表:
Map<Long, JSONObject> m = null;
Map<Long, JSONObject> zeroToFortyNine = new HashMap<>();
Map<Long, JSONObject> fiftyToNinetyNine = new HashMap<>();

m.forEach((k, v) -> {
    if(k < 50) {
        zeroToFortyNine.put(k, v);
    } else {
        fiftyToNinetyNine.put(k, v);
    }
});
m.clear();

1

从你的问题中看来,你似乎不关心分割标准,只是想把它分成两半。下面的解决方案将会相应地起作用。 只需创建一个计数器,并在计数器<(原始哈希映射的大小)/2时插入第一半哈希映射,当计数器>(原始哈希映射的大小)/2时,插入第二半哈希映射。

HashMap<Integer,JSONObject> hmap;
HashMap<Integer,JSONObject> halfhmap1=new HashMap<>();
HashMap<Integer,JSONObject> halfhmap2=new HashMap<>();
int count=0;

for(Map.Entry<Long, JSONObject> entry : map.entrySet()) {
    (count<(hmap.size()/2) ? halfhmap1:halfhmap2).put(entry.getKey(), entry.getValue());
    count++;
}

无法确定。 - Shubham Kadlag
抱歉,我的错。已将其删除。 - Shubham Kadlag
1
不用担心,@Lino已经提供了Java 8的解决方案 ;) 我会使用类似于AtomicBoolean bool = new AtomicBoolean(true); map.forEach((k ,v) -> (bool.getAndSet(bool.get() ^ true) ? sub1 : sub2).put(k,v));这样的代码。 - AxelH
好的,你刚刚改变了条件 ;) 你本可以保留之前的if-else解决方案... - AxelH
你总是可以看到谁先发布了答案。我的逻辑仍然是一样的。我现在只是明白不需要迭代器。 - Shubham Kadlag
显示剩余2条评论

0

您可以使用Guava的一些功能来对集合进行分区。

Map<Object, Object> map = new HashMap<>();

List<List<Map.Entry<Object, Object>>> list = Lists.newArrayList(Iterables.partition(map.entrySet(), map.size() / 2 + 1));

Map<Object, Object> map1 = list.get(0).stream()
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Map<Object, Object> map2 = list.get(1).stream()
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

0
您可以通过计算每个分区的项目数(“recordsPerChunk”)将Map拆分为任意数量的部分。
以下代码应该可以正常工作。
// number of parts for you is 2
int n = 2;
int size = yourMap.size();

int recordsPerChunk = 
  (size % n == 0) ?
    (size / n) :
     ((size / n) + 1);

// Counter
AtomicInteger ai = new AtomicInteger();

// List with n chunks
Collection chunks = 
    yourMap
       .entrySet()
       .stream()
       .collect(Collectors.groupingBy(it -> ai.getAndIncrement() / recordsPerChunk))
       .values();

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