Hashtable против производительности HashMap в однопоточном приложении - PullRequest
2 голосов
/ 02 июля 2011

Я знаю, что Hashtable синхронизирован, поэтому его можно использовать в многопоточном приложении, а HashMap - нет.

Мне интересно, есть ли разница в производительности между этими двумя приложениями в одном потоке.

(Или, когда использовать одно поверх другого?)

Ответы [ 7 ]

4 голосов
/ 02 июля 2011

Если вы хотите потокобезопасную коллекцию, вы можете использовать ConcurrentHashMap или Collections.synchronizedMap () с LinkedHashMap или HashMap.Если вам не нужна многопоточная коллекция, вы можете использовать только последние два.Hashtable был встроен в ретро для поддержки Map с обобщениями, но он также поставляется с множеством устаревших методов, которые делают то же самое или почти то же самое.которые были разработаны позже, будут более чистым решением.Если у вас есть библиотека, для которой требуется Hashtable, вам нужно ее использовать, но в противном случае я бы использовал класс, который делает то, что вам нужно, следуя наилучшей практике с минимумом устаревших методов.Вероятно, будет около 0,5 нас за звонок.Это может или не может быть значительным.

Однако, если вам не нужен тип для поточно-ориентированной безопасности, нет веских причин использовать синхронизированную версию.Если вам нужен тип, обеспечивающий безопасность потоков, вы не можете использовать тип, который не имеет некоторого средства защиты потоков.

2 голосов
/ 02 июля 2011

Принимая то, что @svick упоминает в комментарии. Если вы говорите о Hashtable и HashMap, включенных в Java SDK, то, безусловно, существует разница в производительности, поскольку HashMap не требует использования блоков synchronized, которые имеют служебные данные.

В соответствии с запросом PST, здесь есть некоторые сведения о синхронизированной производительности , а вот что-то более свежее, относительно Java 1.4 против Java 6 на одной машине.

1 голос
/ 07 сентября 2018

Различия

  • HashMap допускает нулевые значения, Hashtable - нет.
  • Hashtable синхронизируется, HashMap - нет.(Но он все еще может использоваться для многопоточного чтения, если он не модифицирован, например, инициализируется один раз при запуске и только чтение из него для некоторых статических кэшей)

Производительность

Вотнесколько однопоточных тестов для их сравнения.5 попыток по 100 миллионных операций (1-я попытка может рассматриваться как разминка), пут - 100% столкновений, получение - 50% попаданий.

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
  • Однопоточность
    HashMap обычно быстрее,Его получить в 2 раза быстрее , чем Hashtable.Однако его значение медленнее на 25% .
  • Параллельное использование
    Hashtable, если не требуются нулевые значения, или Collections.synchronizedMap (new HashMap <> ()), если требуются нулевые значения,Обратите внимание, что Synchronized-HashMap медленнее, чем Hashtable (помещает в 2 раза медленнее, на 50% медленнее)

Код, используемый для теста в качестве 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]);
    }
}
1 голос
/ 02 июля 2011

Поскольку Java 7 заявляет, что в некоторых случаях он может избегать анализа и в некоторых случаях удалять неконтролируемую синхронизацию, я дал ему тест

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

Я не могу вспомнить более простой пример для анализа побега. Однако очевидно, что Java 7 не оптимизирует синхронизацию в моем тесте; каждый synchronized (o){} потребляет некоторое время.

Удивительно, но он потребляет всего около 1 такта процессора, что слишком быстро, чтобы поверить. Он должен содержать как минимум две инструкции сравнения и установки; Доступ к кэш-памяти L1 обычно занимает 10 циклов. По-видимому, начинается оптимизация оборудования.

Это жесткая петля, а не реальное приложение. В общем, слишком сложно обсуждать реальные приложения; трудно анализировать даже для конкретного применения. Тогда мы, вероятно, должны предпочесть HashMap, если это возможно, что, по крайней мере, не будет медленнее, чем Hashtable в любом случае, насколько нам известно.

0 голосов
/ 02 июля 2011

Если в Hashtable и HashMap нет чисел, но несколько лет назад Я сравнил Vector с ArrayList , где существует аналогичная проблема.

0 голосов
/ 02 июля 2011

Я был бы удивлен, если бы вы могли измерить разницу в реальном тесте.Если вы измеряете миллионы операций, может быть, но вы не будете делать миллионы операций, вам будет трудно достичь даже первого миллиона.

0 голосов
/ 02 июля 2011

Да.Это (одна из) точек HashMap, которые не синхронизируются по умолчанию (используйте synchronizedMap (), чтобы сделать его синхронизированным, хотя учтите, что в зависимости от вашего использования простой синхронизации может быть недостаточно для сохранения целостности всех операций, которые вы можете захотеть выполнить).делает).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...