Akash
Akash

Reputation: 840

Splitting a Hashmap into two smaller maps

I have one hashmap with K, V value of and that I want to split into two subMaps.

HashMap<Long,JSONObject>

One way is this I have found that we can use treemap and do subMapping.

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

But the thing is I am not getting subMap for jsonObject and I want to do it with HashMap only.

Thanks

Upvotes: 6

Views: 22529

Answers (6)

bellam
bellam

Reputation: 184

You can split a Map into any number of parts by calculating the number of items per partition ("recordsPerChunk").

The following should work.

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

Upvotes: 0

octavian
octavian

Reputation: 846

You can use some of Guava functionalities which allow to partition a collection.

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

Upvotes: 0

Shubham Kadlag
Shubham Kadlag

Reputation: 2318

It seems from your question that you don't care about criteria for spliting, you just want to split it in half. The below solution will work accordingly. Just create a counter and insert in first half hashmap while counter<(size of original hashmap)/2 and when counter> (size of original hashmap)/2, insert into second half hashmap.

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++;
}

Upvotes: 1

AxelH
AxelH

Reputation: 14572

If you can't use the values or the key to know where to split, simply count the iteration :

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

I used a ternary to increment the counter and select the map. So it will equally split the values on those two maps.

Test:

    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}

That would be easily adapted to split in 3, 4, or any number of maps if wanted :

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}

Upvotes: 2

Lino
Lino

Reputation: 19910

You can make use of the Java 8 Streaming 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
       )
   ));

This collects the map into 2 halfes, the first (map.get(true)) containing all the elements from below the middle and the second (map.get(false)) half containing all the elements from the middle upwards.

Upvotes: 6

ernest_k
ernest_k

Reputation: 45339

You can loop through the set of entries and populate two different maps:

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

Upvotes: 1

Related Questions