caesarkim
caesarkim

Reputation: 91

Hashtable vs HashMap performance in single threaded app

I know that Hashtable is synchronized so it is safe to be used in multithread app and HashMap is not.

I am wondering if there is any performance difference between these two in a single thread app.

(Or, when to use one over the other?)

Upvotes: 6

Views: 7757

Answers (7)

Pla
Pla

Reputation: 497

Differences

  • HashMap allows null values, Hashtable doesn't.
  • Hashtable is synchronized, HashMap is not. (But it can still be used for multithreaded reads if it is not modified, f.e. - initialize once on start and only read from it for some static caches)

Performance

Here are some single-thread tests to compare them. 5 tries of 100 mill ops (1st try may be considered warm-up) put is 100% collisions, get is 50% hits.

1 HashMap put/get -->   419.80  /   354.09  ms
2 HashMap put/get -->   983.02  /   305.54  ms
3 HashMap put/get -->   976.26  /   358.72  ms
4 HashMap put/get -->   989.04  /   375.18  ms
5 HashMap put/get -->   974.13  /   360.73  ms

1 Hashtable put/get -->   776.97  /   708.39  ms
2 Hashtable put/get -->   776.26  /   736.23  ms
3 Hashtable put/get -->   794.01  /   740.07  ms
4 Hashtable put/get -->   784.23  /   734.40  ms
5 Hashtable put/get -->   782.45  /   729.48  ms

1 Synced-HashMap put/get -->  1523.61  /  1215.63  ms
2 Synced-HashMap put/get -->  1491.59  /  1090.83  ms
3 Synced-HashMap put/get -->  1442.67  /  1095.62  ms
4 Synced-HashMap put/get -->  1439.19  /  1082.57  ms
5 Synced-HashMap put/get -->  1450.04  /  1101.53  ms
  • Single-thread
    HashMap is generally faster. Its get is 2x faster than Hashtable. However, its put is 25% slower.
  • Concurrent use
    Hashtable if no null values are required, or Collections.synchronizedMap(new HashMap<>()) if nulls are required. Note that Synchronized-HashMap is slower than Hashtable (puts 2x slower, gets 50% slower)

The code used for the test as a JUnit:

import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import org.junit.Test;

public class MapsPerfTest {

    @Test
    public void testMaps() {
        testMap("HashMap", new HashMap<>());
        testMap("Hashtable", new Hashtable<>());
        testMap("Synced-HashMap", Collections.synchronizedMap(new HashMap<>()));
    }

    void testMap(String name, Map<Integer, String> h) {
        for(int i=1; i<=tries; ++i) {
            long t1 = timeit(() -> testMapPut(h));
            long t2 = timeit(() -> testMapGet(h));
            System.out.println(String.format("%d %s put/get -->  %7.2f  /  %7.2f  ms",
                    i, name, t1/1000/1000.0, t2/1000/1000.0));
        }
    }

    long timeit(Runnable r) {
        System.gc();
        long t = System.nanoTime();
        r.run();
        return System.nanoTime() - t;
    }

    static final int tries = 5;
    static final int count = 100000000;

    static final String VALUE = "-";

    static final int putSpace = 100;
    static final int getSpace = putSpace*2;
    static final Integer[] numbers = new Integer[getSpace+1];

    static {
        for(int i=getSpace; i>=0; --i)
            numbers[i] = i;
    }

    void testMapPut(Map<Integer, String> m) {
        for(int i=count; i>0; --i)
            m.put(numbers[i%putSpace], VALUE);
    }

    void testMapGet(Map<Integer, String> m) {
        for(int i=count; i>0; --i)
            m.get(numbers[i%getSpace]);
    }
}

Upvotes: 6

dmeister
dmeister

Reputation: 35604

If have no numbers on Hashtable vs. HashMap, but some years ago I compared Vector with ArrayList, where there is a similar issue.

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533472

If you want a thread safe collection you can use ConcurrentHashMap or Collections.synchronizedMap() with LinkedHashMap or HashMap. If you don't need a thread safe collection you can use just the last two. Hashtable has been retro fitted to support Map with generics but it also comes with alot of legacy methods which do the same thing or much the same thing.

Hashtable can be used, however IMHO using one of the many other options which have been developed later would be a cleaner solution. If you have a library which needs a Hashtable, then you need to use it, but otherwise I would use a class which does what you need, follows best practice with a minimum of legacy methods.

The performance difference is likely to be about 0.5 us per call. This may or may not be significant.

However, if you don't need a type to be thread safe, there is no good reason to use the synchronized version. If you need a type to be thread safe, you can't use a type which is not without some thread safety guard.

Upvotes: 5

user207421
user207421

Reputation: 310860

I would be astonished if you could even measure a difference in a real-world test. If you measure zillions of operations, maybe, but you won't be doing zillions, you'll be hard pressed to attain even the first million.

Upvotes: 0

irreputable
irreputable

Reputation: 45433

Since Java 7 claims that it may do escape analysis, and remove uncontended sync in some cases, I gave it a test

public static void main(String[] args)
{
    for(int i=0; i<100; i++)
    {
        System.out.println("-------------------");
        testS();
    }
}
static int N = 100_000_000;
static void testS()
{
    Object o = new Object();
    long t0 = System.nanoTime();
    for(int i=0; i<N; i++)
        synchronized (o){}
    long t = System.nanoTime() - t0;
    System.out.printf("time: %,d%n", t);
}

I can't think of a simpler example for escape analysis. However, obviously Java 7 does not optimize the sync off in my test; each synchronized (o){} consume some time.

Amazingly, it only consumes about 1 CPU cycle, which is too fast to believe. It should contain at least two compare-and-set instructions; accessing L1 cache normally takes 10 cycles. Apparently there's some hardware optimization kicking in.

This is a tight loop, not real application. It's too difficult to discuss real apps in general; difficult to analyze even for a concrete application. Then we probably should prefer HashMap if possible, which at least won't be slower than Hashtable in any case, as far as we know.

Upvotes: 1

nicholas.hauschild
nicholas.hauschild

Reputation: 42849

Taking what @svick mentions in the comment. If you are talking about the Hashtable and HashMap included with the Java SDK, there is definitely a performance difference, as HashMap doesn't have to use the synchronized blocks, which have an overhead.

As per pst's request, here is some reading about synchronized performance and here is something a bit more recent, regarding Java 1.4 vs Java 6 on one machine.

Upvotes: 2

Mike Tunnicliffe
Mike Tunnicliffe

Reputation: 10762

Yes. That's the (one of) the points of HashMap not being synchronized by default (use synchronizedMap() to make it synchronized; although note that depending on your usage, just simple synchronization may not be enough to keep integrity over all operations you might want to do).

Upvotes: 0

Related Questions