alex berne
alex berne

Reputation: 699

Why is creating a HashMap faster than creating an Object[]?

I've tried to build my own Map to increase the performance for a special environment, and I realized something pretty interesting: Creating a new Hashmap<Integer,String>(2000) is faster than new Object[2000] - no matter in which order I execute these commands. That's pretty confusing to me, esp. because the Hashmap constructor contains a table = new Entry[capacity], according to this. Is there something wrong with my testbench?

public static void test(int amm){ //amm=1_000_000
    Map<Integer,String> m1 = null;
    Object[] arr = null;

    long time = System.nanoTime();
    for(int i = 0; i < amm; i++){
        m1 = new HashMap<Integer, String>(2000);
    }
    System.out.println("m1: " + (System.nanoTime() - time)); //m1: 70_455_065

    time = System.nanoTime();
    for(int i = 0; i < amm; i++){
        arr = new Object[2000];
    }
    System.out.println("arr: " + (System.nanoTime() - time)); //arr: 1_322_473_803
}

I'd love to see the results of testing on another computer. I've got no clue why creating a HashMap is 10 times faster than creating a Object[].

Upvotes: 44

Views: 3298

Answers (2)

Amir Raminfar
Amir Raminfar

Reputation: 34169

If you look at the implementation of HashMap, the constructor looks like:

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);

    this.loadFactor = loadFactor;
    threshold = initialCapacity;
    init();
}

And init() looks like:

/**
 * Initialization hook for subclasses. This method is called
 * in all constructors and pseudo-constructors (clone, readObject)
 * after HashMap has been initialized but before any entries have
 * been inserted.  (In the absence of this method, readObject would
 * require explicit knowledge of subclasses.)
 */
void init() {
}

So initialCapacity doesn't actually get used to create an array. Where does it get used? Look at the put() method.

public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    // hidden
} 

When doing a put, the array is actually created. I didn't show inflateTable() but it does some math and initializes the array.

Upvotes: 51

Greg Hewgill
Greg Hewgill

Reputation: 993901

An empty HashMap object is much smaller than an array of 2000 Object references. Even though you pass 2000 to the initialCapacity parameter of the HashMap constructor, it's not actually creating 2000 spaces for objects yet.

Upvotes: 21

Related Questions