En Java, ConcurrentHashMap
existe una mejor multithreading
solución. Entonces, ¿cuándo debería usar ConcurrentSkipListMap
? ¿Es una redundancia?
¿Son comunes los aspectos de subprocesos múltiples entre estos dos?
fuente
En Java, ConcurrentHashMap
existe una mejor multithreading
solución. Entonces, ¿cuándo debería usar ConcurrentSkipListMap
? ¿Es una redundancia?
¿Son comunes los aspectos de subprocesos múltiples entre estos dos?
Estas dos clases varían de varias formas.
ConcurrentHashMap no garantiza * el tiempo de ejecución de sus operaciones como parte de su contrato. También permite ajustar ciertos factores de carga (aproximadamente, el número de subprocesos que lo modifican simultáneamente).
ConcurrentSkipListMap , por otro lado, garantiza un rendimiento promedio de O (log (n)) en una amplia variedad de operaciones. Tampoco admite el ajuste por simultaneidad. ConcurrentSkipListMap
también tiene una serie de operaciones que ConcurrentHashMap
no las tiene: techoEntry / Key, floorEntry / Key, etc. También mantiene un orden de clasificación, que de otro modo tendría que calcularse (a un costo considerable) si estuviera usando un ConcurrentHashMap
.
Básicamente, se proporcionan diferentes implementaciones para diferentes casos de uso. Si necesita una adición rápida de un par de clave / valor y una búsqueda rápida de una sola clave, use el HashMap
. Si necesita un recorrido en orden más rápido y puede pagar el costo adicional de la inserción, use la extensión SkipListMap
.
* Aunque espero que la implementación esté aproximadamente en línea con las garantías generales del mapa hash de la inserción / búsqueda de O (1); ignorando el re-hash
Ordenado, navegable y concurrente
Consulte Lista de omisión para obtener una definición de la estructura de datos.
A
ConcurrentSkipListMap
almacena elMap
en el orden natural de sus claves (o algún otro orden de claves que usted defina). Por lo que tendrá más lentosget
/put
/contains
operaciones que unaHashMap
, pero para compensar este es compatible con losSortedMap
,NavigableMap
yConcurrentNavigableMap
las interfaces.fuente
En términos de rendimiento,
skipList
cuando se usa como mapa, parece ser de 10 a 20 veces más lento. Aquí está el resultado de mis pruebas (Java 1.8.0_102-b14, win x32)Benchmark Mode Cnt Score Error Units MyBenchmark.hasMap_get avgt 5 0.015 ? 0.001 s/op MyBenchmark.hashMap_put avgt 5 0.029 ? 0.004 s/op MyBenchmark.skipListMap_get avgt 5 0.312 ? 0.014 s/op MyBenchmark.skipList_put avgt 5 0.351 ? 0.007 s/op
Y además de eso, caso de uso en el que comparar uno con otro realmente tiene sentido. Implementación de la caché de los últimos elementos utilizados recientemente utilizando ambas colecciones. Ahora la eficiencia de skipList parece ser aún más dudosa.
MyBenchmark.hashMap_put1000_lru avgt 5 0.032 ? 0.001 s/op MyBenchmark.skipListMap_put1000_lru avgt 5 3.332 ? 0.124 s/op
Aquí está el código para JMH (ejecutado como
java -jar target/benchmarks.jar -bm avgt -f 1 -wi 5 -i 5 -t 1
)static final int nCycles = 50000; static final int nRep = 10; static final int dataSize = nCycles / 4; static final List<String> data = new ArrayList<>(nCycles); static final Map<String,String> hmap4get = new ConcurrentHashMap<>(3000, 0.5f, 10); static final Map<String,String> smap4get = new ConcurrentSkipListMap<>(); static { // prepare data List<String> values = new ArrayList<>(dataSize); for( int i = 0; i < dataSize; i++ ) { values.add(UUID.randomUUID().toString()); } // rehash data for all cycles for( int i = 0; i < nCycles; i++ ) { data.add(values.get((int)(Math.random() * dataSize))); } // rehash data for all cycles for( int i = 0; i < dataSize; i++ ) { String value = data.get((int)(Math.random() * dataSize)); hmap4get.put(value, value); smap4get.put(value, value); } } @Benchmark public void skipList_put() { for( int n = 0; n < nRep; n++ ) { Map<String,String> map = new ConcurrentSkipListMap<>(); for( int i = 0; i < nCycles; i++ ) { String key = data.get(i); map.put(key, key); } } } @Benchmark public void skipListMap_get() { for( int n = 0; n < nRep; n++ ) { for( int i = 0; i < nCycles; i++ ) { String key = data.get(i); smap4get.get(key); } } } @Benchmark public void hashMap_put() { for( int n = 0; n < nRep; n++ ) { Map<String,String> map = new ConcurrentHashMap<>(3000, 0.5f, 10); for( int i = 0; i < nCycles; i++ ) { String key = data.get(i); map.put(key, key); } } } @Benchmark public void hasMap_get() { for( int n = 0; n < nRep; n++ ) { for( int i = 0; i < nCycles; i++ ) { String key = data.get(i); hmap4get.get(key); } } } @Benchmark public void skipListMap_put1000_lru() { int sizeLimit = 1000; for( int n = 0; n < nRep; n++ ) { ConcurrentSkipListMap<String,String> map = new ConcurrentSkipListMap<>(); for( int i = 0; i < nCycles; i++ ) { String key = data.get(i); String oldValue = map.put(key, key); if( (oldValue == null) && map.size() > sizeLimit ) { // not real lru, but i care only about performance here map.remove(map.firstKey()); } } } } @Benchmark public void hashMap_put1000_lru() { int sizeLimit = 1000; Queue<String> lru = new ArrayBlockingQueue<>(sizeLimit + 50); for( int n = 0; n < nRep; n++ ) { Map<String,String> map = new ConcurrentHashMap<>(3000, 0.5f, 10); lru.clear(); for( int i = 0; i < nCycles; i++ ) { String key = data.get(i); String oldValue = map.put(key, key); if( (oldValue == null) && lru.size() > sizeLimit ) { map.remove(lru.poll()); lru.add(key); } } } }
fuente
Cuando (a) necesita mantener las claves ordenadas, y / o (b) necesita las características de primer / último, cabeza / cola y submapa de un mapa navegable.
La
ConcurrentHashMap
clase implementa laConcurrentMap
interfaz, al igual que lo haceConcurrentSkipListMap
. Pero si también quieres los comportamientos deSortedMap
yNavigableMap
, usaConcurrentSkipListMap
ConcurrentHashMap
ConcurrentSkipListMap
Aquí hay una tabla que lo guía a través de las principales características de las diversas
Map
implementaciones incluidas con Java 11. Haga clic / toque para ampliar.Tenga en cuenta que puede obtener otras
Map
implementaciones y estructuras de datos similares de otras fuentes, como Google Guava .fuente
String
clases para las clases y la interfaz relacionadas .Según las cargas de trabajo, ConcurrentSkipListMap podría ser más lento que TreeMap con métodos sincronizados como en KAFKA-8802 si se necesitan consultas de rango.
fuente
ConcurrentHashMap: cuando desee obtener / poner basado en índices multiproceso, solo se admiten operaciones basadas en índices. Get / Put son de O (1)
ConcurrentSkipListMap: más operaciones que solo obtener / poner, como n elementos de arriba / abajo ordenados por clave, obtener la última entrada, buscar / recorrer el mapa completo ordenado por clave, etc. La complejidad es de O (log (n)), así que el rendimiento de la puesta no tan bueno como ConcurrentHashMap. No es una implementación de ConcurrentNavigableMap con SkipList.
Para resumir, use ConcurrentSkipListMap cuando desee realizar más operaciones en el mapa que requieran características ordenadas en lugar de simplemente obtener y colocar.
fuente