0

I have implemented a method to split HashMap type container into same size of chunks. However, this method is not enough for my application, now. It restrict my application to scale. Thus, I have tried to change its generic type into <T extends Map<?,?>. However, whenever I change this new type, I have faced with more hard problem like "how to create a generic type array or instance". I have not get rid of even if I have searched the web. How can I change my method to new Generic type?

My method;

public static <Type1, Type2> 
HashMap<Type1, Type2>[] 
split(HashMap<Type1, Type2> hashTable, int chunkSize) {     

    int numberOfChunks = ((int) Math.ceil( hashTable.size() / (double)chunkSize))

    HashMap<Type1, Type2> []chunks = new HashMap[numberOfChunks]; 

    Set<Type1> keys = hashTable.keySet();

    int i = 0, j = -1;
    for(Type1 key : keys){
        if((i % chunkSize) == 0 ){
            j = j + 1;
            chunks[j] = new HashMap<Type1, Type2>();
        }
        chunks[j].put(key, (hashTable.get(key)));
        i = i + 1;
    }

    return chunks;
}

I want to change this method to

public static <T extends Map<?,?>> 
T[] 
split(T hashTable, int chunkSize) {     

    int numberOfChunks = ((int) Math.ceil( hashTable.size() / (double)chunkSize))

    T []chunks = new T[numberOfChunks];  // Problem I

    Set<Type1> keys = hashTable.keySet();

    int i = 0, j = -1;
    for(Type1 key : keys){
        if((i % chunkSize) == 0 ){
            j = j + 1;
            chunks[j] = new T();  // Problem II
        }
        chunks[j].put(key, (hashTable.get(key)));
        i = i + 1;
    }

    return chunks;
}

How can I revolve to this new generic type, or how can I fix source of these problem, marked with // Problem?

2 Answers2

0

I am sorry, but why don't you try something like this

public static Map[] split(Map hashTable, int chunkSize) {     

    int numberOfChunks = ((int) Math.ceil( hashTable.size() / (double)chunkSize));

    Map[] chunks = new HashMap[numberOfChunks]; 

    Set keys = hashTable.keySet();

    int i = 0, j = -1;
    for(Object key : keys){
        if((i % chunkSize) == 0 ){
            j = j + 1;
            chunks[j] = new HashMap();
        }
        chunks[j].put(key, (hashTable.get(key)));
        i = i + 1;
    }

    return chunks;
}

you're just splitting your Map, there's no need to enforce types here.

Leo
  • 6,177
  • 3
  • 28
  • 48
  • It is part of hobby project. I am trying to learn as much as possible and try new things. Thanks for your solution. –  Jul 30 '14 at 18:01
0

What you are trying to achive wil not be easy since you will have 2 problem that will need solving. But first, change that method signature to:

public static <K, V, M extends Map<K, V>> M[] split(M table, int chunkSize)

(K for Key, V for Value and M for Map) this way, you can split any map you want.

The first problem is that you can't (easilly) create a generic array, but you can use this workaround.

The other problem is that you can't simply create new HashMap<>() to fill your array (what if your map isn't a HashMap ?), So you can:

  1. Use Supplier<? super M> if you use Java 1.8 (or old-fasion interface for 1.7- versions)
  2. Access table's class' constructor via reflection

Choosing 1, I have modifiesd your code to this:

public static <K, V, M extends Map<K, V>> M[] split(M table, int chunkSize,
        Supplier<? extends M> mapCreator) {

    int numberOfChunks = (int) Math.ceil(table.size() / (double) chunkSize);

    M[] chunks = (M[]) Array.newInstance(table.getClass(), numberOfChunks);

    Set<K> keys = table.keySet();

    int i = 0, j = -1;
    for (K key : keys) {
        if ((i % chunkSize) == 0) {
            j = j + 1;
            chunks[j] = mapCreator.get();
        }
        chunks[j].put(key, (table.get(key)));
        i = i + 1;
    }

    return chunks;
}

With test code:

static class MyTreeMap<K, V> extends TreeMap<K, V> {
}

public static void main(String[] main) {
    HashMap<String, Integer> map1 = new HashMap<>();
    map1.put("one", 1);
    map1.put("two", 2);
    map1.put("three", 3);
    HashMap[] res = split(map1, 2, HashMap::new);
    Arrays.stream(res).forEach(System.out::println);

    MyTreeMap<Double, Boolean> map2 = new MyTreeMap<>();
    map2.put(.5, true);
    map2.put(5d, false);
    MyTreeMap[] res2 = split(map2, 1, MyTreeMap::new);
    Arrays.stream(res2).forEach(System.out::println);
}

The output is:

{one=1, two=2}
{three=3}
{0.5=true}
{5.0=false}

So it seems to be working.

Community
  • 1
  • 1
kajacx
  • 10,561
  • 4
  • 35
  • 60