我什么时候应该使用ConcurrentSkipListMap?

发布于 2024-08-13 04:40:45 字数 112 浏览 4 评论 0原文

在 Java 中,ConcurrentHashMap 提供了更好的多线程解决方案。那么什么时候应该使用ConcurrentSkipListMap呢?是不是冗余?

这两者之间的多线程方面是否共同?

In Java, ConcurrentHashMap is there for better multithreading solution. Then when should I use ConcurrentSkipListMap? Is it a redundancy?

Does multithreading aspects between these two are common?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

一个人的旅程 2024-08-20 04:40:45

这两个类在几个方面有所不同。

ConcurrentHashMap 不保证*作为合同一部分的其操作的运行时间。它还允许调整某些负载因子(大致是同时修改它的线程数)。

另一方面,ConcurrentSkipListMap,保证各种操作的平均 O(log(n)) 性能。它也不支持并发调整。 ConcurrentSkipListMap 还具有许多 ConcurrentHashMap 没有的操作:ceilingEntry/Key、floorEntry/Key 等。它还维护排序顺序,否则必须是如果您使用 ConcurrentHashMap,则计算(花费大量费用)。

基本上,针对不同的用例提供了不同的实现。如果您需要快速单键/值对添加和快速单键查找,请使用HashMap。如果您需要更快的有序遍历,并且可以承受额外的插入成本,请使用 SkipListMap

*虽然我预计实现大致符合 O(1) 插入/查找的一般哈希映射保证;忽略重新哈希

These two classes vary in a few ways.

ConcurrentHashMap does not guarantee* the runtime of its operations as part of its contract. It also allows tuning for certain load factors (roughly, the number of threads concurrently modifying it).

ConcurrentSkipListMap, on the other hand, guarantees average O(log(n)) performance on a wide variety of operations. It also does not support tuning for concurrency's sake. ConcurrentSkipListMap also has a number of operations that ConcurrentHashMap doesn't: ceilingEntry/Key, floorEntry/Key, etc. It also maintains a sort order, which would otherwise have to be calculated (at notable expense) if you were using a ConcurrentHashMap.

Basically, different implementations are provided for different use cases. If you need quick single key/value pair addition and quick single key lookup, use the HashMap. If you need faster in-order traversal, and can afford the extra cost for insertion, use the SkipListMap.

*Though I expect the implementation is roughly in line with the general hash-map guarantees of O(1) insertion/lookup; ignoring re-hashing

双马尾 2024-08-20 04:40:45

排序、可导航和并发

有关数据结构的定义,请参阅跳过列表

< code>ConcurrentSkipListMap 存储 Map 按照其键的自然顺序(或您定义的其他键顺序)。因此,它的 get/put/contains 操作比 HashMap,但为了抵消它支持SortedMapNavigableMapConcurrentNavigableMap 接口。

Sorted, navigable, and concurrent

See Skip List for a definition of the data structure.

A ConcurrentSkipListMap stores the Map in the natural order of its keys (or some other key order you define). So it'll have slower get/put/contains operations than a HashMap, but to offset this it supports the SortedMap, NavigableMap, and ConcurrentNavigableMap interfaces.

九厘米的零° 2024-08-20 04:40:45

那么什么时候应该使用ConcurrentSkipListMap?

当您 (a) 需要对键进行排序,和/或 (b) 需要可导航地图的第一个/最后一个、头部/尾部和子地图功能时。

ConcurrentHashMap 类实现 ConcurrentMap 接口,ConcurrentSkipListMap。但如果您还想要 SortedMapNavigableMap,使用 ConcurrentSkipListMap

ConcurrentHashMap

  • ❌ 已排序
  • ❌ 可导航
  • ✅ 并发

ConcurrentSkipListMap

  • ✅ 已排序
  • ✅ 可导航
  • ✅ 并发

这是一个表格,指导您了解各种 Map 与 Java 11 捆绑在一起的实现。单击/点击进行缩放。

地图表Java 11 中的实现,比较它们的功能

请记住,您可以获得其他 Map 实现以及类似的数据结构,来自其他来源,例如 Google Guava

Then when should I use ConcurrentSkipListMap?

When you (a) need to keep keys sorted, and/or (b) need the first/last, head/tail, and submap features of a navigable map.

The ConcurrentHashMap class implements the ConcurrentMap interface, as does ConcurrentSkipListMap. But if you also want the behaviors of SortedMap and NavigableMap, use ConcurrentSkipListMap

ConcurrentHashMap

  • ❌ Sorted
  • ❌ Navigable
  • ✅ Concurrent

ConcurrentSkipListMap

  • ✅ Sorted
  • ✅ Navigable
  • ✅ Concurrent

Here is table guiding you through the major features of the various Map implementations bundled with Java 11. Click/tap to zoom.

Table of map implementations in Java 11, comparing their features

Keep in mind that you can obtain other Map implementations, and similar such data structures, from other sources such as Google Guava.

笔落惊风雨 2024-08-20 04:40:45

在性能方面,skipList 当用作 Map 时 - 似乎慢了 10-20 倍。这是我的测试结果(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

除此之外 - 比较一对一确实有意义的用例。使用这两个集合实现最近使用的项目的缓存。现在,skipList 的效率看起来更加可疑。

MyBenchmark.hashMap_put1000_lru      avgt    5  0.032 ?  0.001   s/op
MyBenchmark.skipListMap_put1000_lru  avgt    5  3.332 ?  0.124   s/op

以下是 JMH 的代码(执行为 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);
            }
        }
    }
}

In terms of performance, skipList when is used as Map - appears to be 10-20 times slower. Here is the result of my tests (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

And additionally to that - use-case where comparing one-to-another really makes sense. Implementation of the cache of last-recently-used items using both of these collections. Now efficiency of skipList looks to be event more dubious.

MyBenchmark.hashMap_put1000_lru      avgt    5  0.032 ?  0.001   s/op
MyBenchmark.skipListMap_put1000_lru  avgt    5  3.332 ?  0.124   s/op

Here is the code for JMH (executed as 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);
            }
        }
    }
}
通知家属抬走 2024-08-20 04:40:45

基于工作负载,ConcurrentSkipListMap 可能比使用同步方法的 TreeMap 慢,如 KAFKA-8802 如果需要范围查询。

Based on workloads ConcurrentSkipListMap could be slower than TreeMap with synchronized methods as in KAFKA-8802 if range queries are needed.

世态炎凉 2024-08-20 04:40:45

ConcurrentHashMap :当您想要基于多线程索引的 get/put 时,仅支持基于索引的操作。 Get/Put 的复杂度为 O(1)

ConcurrentSkipListMap :不仅仅是 get/put 的更多操作,例如按键排序顶部/底部 n 个项目、获取最后一个条目、获取/遍历按键排序的整个映射等。复杂度为 O(log( n)), 所以put性能不如ConcurrentHashMap。它不是带有 SkipList 的 ConcurrentNavigableMap 的实现。

总而言之,当您想要在需要排序功能的地图上执行更多操作而不仅仅是简单的获取和放置时,请使用 ConcurrentSkipListMap。

ConcurrentHashMap : when u want multithreaded index based get/put, only index based operations are supported. Get/Put are of O(1)

ConcurrentSkipListMap : More operations than just get/put, like sorted top/bottom n items by key, get last entry, fetch/traverse whole map sorted by key etc. Complexity is of O(log(n)), So put performance is not as great as ConcurrentHashMap. It't an implementation of ConcurrentNavigableMap with SkipList.

To summarize use ConcurrentSkipListMap when you want to do more operations on map requiring sorted features rather than just simple get and put.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文