Reputation: 91
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
Reputation: 497
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
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
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
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
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
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
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
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