Java 分块阅读ConcurrentHashMap(或类似内容)

Java 分块阅读ConcurrentHashMap(或类似内容),java,concurrency,hashmap,java.util.concurrent,Java,Concurrency,Hashmap,Java.util.concurrent,我有一个程序,它有一个ConcurrentHashMap,不同的线程可以在该映射中添加/删除项目 我很想知道什么是阅读25个项目的地图的最佳方法。 我想做的是这样的:用户点击一个按钮,然后从地图上读取25个项目(无论顺序如何)。之后,他可以点击“下一步”按钮,阅读另外25项内容(与前25项不同),以此类推 我不确定是否可以使用ConcurrentHashMap来实现这一点。我不想使用数据库,我想把它保存在内存中。 我不认为将Map转换为ArrayList会有帮助,因为大多数情况下项目都是从Map

我有一个程序,它有一个
ConcurrentHashMap
,不同的线程可以在该映射中添加/删除项目

我很想知道什么是阅读25个项目的地图的最佳方法。 我想做的是这样的:用户点击一个按钮,然后从地图上读取25个项目(无论顺序如何)。之后,他可以点击“下一步”按钮,阅读另外25项内容(与前25项不同),以此类推

我不确定是否可以使用
ConcurrentHashMap
来实现这一点。我不想使用数据库,我想把它保存在内存中。 我不认为将
Map
转换为
ArrayList
会有帮助,因为大多数情况下项目都是从Map中添加/删除的

我对任何解决方案都持开放态度,即使是第三方库

更新:我也没有绑定到
ConcurrentHashMap
。我只是在寻找最好的解决办法

更新2:它们是
String


感谢您,因为字符串键是严格排序的,所以这是一种方法

  • 它是和,并且经常可以用来代替ConcurrentHashMap
  • 获取
    /
    放置
    /
    删除
    O(log N)
    一样快
  • 作为奖励,您将免费获得自然遍历顺序
要从ConcurrentSkipListMap获取下一页,请使用上一页的最后一个键作为锚点调用
tailMap
,然后从结果子映射构造迭代器或流:

    return map.tailMap(lastKey, false).entrySet()
            .stream()
            .limit(pageSize)
            .collect(Collectors.toList());
请注意,
tailMap
即使从地图中删除了定位键,也会成功。迭代将从比锚点大的下一个关键点开始



如果键的顺序不严格,或者需要O(1)复杂度,那么您可能更喜欢另一种建议—按单元索引顺序遍历的开放寻址哈希表。然而,在标准Java类中没有这样的实现。这种映射的线程安全通常是通过锁定实现的。

< P>如果您不想使用CONRUSTHASHMAP,那么可以考虑使用Link KeHasMMAP,并同步它。 linkedhashmap按插入顺序存储,因此您可以对其进行迭代。 但是,您需要保留一个lastViewedElement,当它被删除时,用一个新的有效值更新它,以便您可以从那里进行迭代

它应该正确同步(可能是手动同步),因为它不是线程安全的。粗略的实现是:
(我是通过head而不是IDE来完成这项工作的,所以它可能会遗漏一些东西。而且我的泛型没有达到标准,而且您可能根本不需要泛型…)

公共类MyMagicMap实现映射
私有LinkedHashMap innerMap=新LinkedHashMap();
X lastKey=null;
@凌驾
公共作废认沽权(X键,Y值){
已同步(MyMagicMap.class){
if(innerMap.contains(键)){
删除(键);
}
innerMap.put(键、值);
}
}
@凌驾
公共Y删除(X键){
已同步(MyMagicMap.class){
if(key.equals)(lastKey){
lastKey=getKeyBefore(lastKey);
}
返回innerMap.remove(键);
}
}
私有X getKeyBefore(X oldLastKey){
Iterator it=innerMap.KeySet.Iterator();
for(X键:it){
if(key.equals(oldLastKey)){
返回它。下一步();
}
}
}
公共地图getNextBatch(整数计数){
已同步(MyMagicMap.class){
Map resultMap=new HashMap();
迭代器it=innerMap.keySet().Iterator();
X lastKey=扫描FORLASTKEY(it);
if(lastKey==null){
返回结果图;
}
resultMap.put(lastKey),innerMap.get(key);
for(int i;i
需要注意的是,在相关块上有线程同步,只有一个线程应该执行此操作。
还请注意,delete方法的性能很差(O(N))
如果你想做得更快,你必须实现你自己的LinkedHashMap,它允许你有效地确定下一行。听起来很难,直到你意识到你只需要一个HashMap>,三元组的键值是previous和next。再次同步put和remove操作,这是为了保持li的完整性圣,即

void put(X key, Y value) {
    // synchronize this
    Triplet<X,Y,X> lastValueInMap = innerMap.get(endOfMapKey);
    lastValueInMap.rightElement = key;
    innerMap.put(X, new Triplet(endOfMapKey, value, null);
    endOfMapKey = key;
}
Y remove(X key) {
    // synchronize this.
    Triplet<X,Y,X> valueTriplet = innerMap.remove(key);
    if(valueTriplet.left != null)
        if(valueTriplet.right != null) {
            innerMap.get(valueTriplet.left).right = valueTriplet.right;
            innerMap.get(valueTriplet.right).left=valueTriplet.left;
        } else
        // etc.
     }
}
void put(X键,Y值){
//同步这个
三元组lastValueInMap=innerMap.get(endOfMapKey);
lastValueInMap.rightElement=键;
put(X,新的三元组(endOfMapKey,value,null);
endOfMapKey=key;
}
Y移除(X键){
//同步这个。
三元组值三元组=innerMap.remove(键);
if(valueTriplet.left!=null)
if(valueTriplet.right!=null){
get(valueTriplet.left).right=valueTriplet.right;
get(valueTriplet.right).left=valueTriplet.left;
}否则
//等等。
}
}
最后要注意的是put方法,它显式地删除已经存在的键,而不是更新值。由于LinkedHashMap的更新不会修改插入顺序,这意味着如果不删除,任何已经在批中读取并随后更新的键K将永远不会显示在ne中
void put(X key, Y value) {
    // synchronize this
    Triplet<X,Y,X> lastValueInMap = innerMap.get(endOfMapKey);
    lastValueInMap.rightElement = key;
    innerMap.put(X, new Triplet(endOfMapKey, value, null);
    endOfMapKey = key;
}
Y remove(X key) {
    // synchronize this.
    Triplet<X,Y,X> valueTriplet = innerMap.remove(key);
    if(valueTriplet.left != null)
        if(valueTriplet.right != null) {
            innerMap.get(valueTriplet.left).right = valueTriplet.right;
            innerMap.get(valueTriplet.right).left=valueTriplet.left;
        } else
        // etc.
     }
}