【问题标题】:Are there any data structures which preserves iteration order and also drop old records?是否有任何数据结构可以保留迭代顺序并删除旧记录?
【发布时间】:2017-01-20 00:13:58
【问题描述】:

我有一个用例,我想从多个线程将条目填充到数据结构中,因此它必须是线程安全的,并且在达到特定大小后开始删除旧记录。而且我还想以相同的插入顺序迭代数据结构。

所以我决定在这里使用Guava Cache,但令我惊讶的是,Guava asMap() 方法不会以任何特定顺序返回元素。

private final Cache<Integer, Integer> cache =
      CacheBuilder.newBuilder().maximumSize(10)
          .removalListener(
              RemovalListeners.asynchronous(new CustomListener(), executorService)
          ).build();

cache.put(1, 1);
cache.put(2, 2);
cache.put(3, 3);
cache.put(4, 4);
cache.put(5, 5);
cache.put(6, 6);

for (Entry<Integer, Integer> entry : cache.asMap().entrySet()) {
  System.out.println(entry.getKey() + "=" + entry.getValue());
}

输出:

2=2
6=6
1=1
4=4
3=3
5=5

我可以在这里使用什么其他有效的数据结构,它可以为我保留迭代顺序,并且在达到大小时可靠删除旧记录,并在我的删除侦听器中有一些回调能弄清楚哪些记录被丢弃了吗?

任何示例都会有很大帮助。我正在使用 Java 7,还不能切换到 Java 8。

所以我应该能够在迭代时得到这样的东西,而且它应该自动删除旧记录:

1=1
2=2
3=3
4=4
5=5
6=6

【问题讨论】:

  • 您能否详细谈谈您希望如何访问数据结构中的元素?会这样吗?用钥匙?随机?在前面?
  • 以相同的顺序插入,所以1 and 1首先插入,所以我想在迭代时先得到它。编辑了问题。
  • 所以你从数据结构中获取的唯一方法就是迭代它?你可以使用队列数据结构
  • 是的。顺便说一句,还有其他方法也可以从数据结构中获取吗?只是好奇。
  • 是的,Hashmap 之类的东西是将事物配对在一起以进行快速查找的方式

标签: java data-structures guava


【解决方案1】:

对于 Java 7,您可以使用 Caffeine 的前身 ConcurrentLinkedHashMap

ConcurrentMap<Integer, Integer> cache =
        new ConcurrentLinkedHashMap.Builder<Integer, Integer>()
                .maximumWeightedCapacity(10)
                .build();

cache.put(1, 1);
cache.put(2, 2);
cache.put(3, 3);
cache.put(4, 4);
cache.put(5, 5);
cache.put(6, 6);

for (Entry<Integer, Integer> entry : cache.entrySet()) {
    System.out.println(entry.getKey() + "=" + entry.getValue());
}

输出

1=1
2=2
3=3
4=4
5=5
6=6

更多详情请见ExampleUsage · ben-manes/concurrentlinkedhashmap Wiki

【讨论】:

  • CLHM 是访问顺序的,所以这可能不符合他的需要。同时需要高度并发的映射和插入顺序是非常不寻常的。这很容易编写,但通常不是真正需要的。他很可能只需要一个同步的LinkedHashMap 作为fifo 缓存,并且可以接受有限的并发性。
  • 啊,我没有意识到这是访问命令。谢谢@BenManes。
  • @BenManes CLHM 有什么问题?我认为这最适合我吗?我主要是在等你来看看这个问题给一些反馈:)
  • @user5447339 该策略保持按访问顺序 (LRU),因为每次读取都是写入,因此很难同时进行。但是你想要插入顺序(FIFO),它很容易同时执行,但命中率很差。由于高并发往往意味着非确定性访问,因此以插入顺序迭代 ConcurrentMap 几乎没有意义。这就是为什么你不会在 Github 上看到一个浮动版本或被广泛使用的版本。大多数情况下,LHM 对于这些情况已经足够了。我可以帮你写一个(最容易通过电子邮件聊天),但我同意其他怀疑 LHM 适合的人的观点。
【解决方案2】:

保留迭代顺序的常见数据结构是,

  • 队列
  • 双端队列
  • 链表等。

但是,最适合您需求的解决方案是使用 deque,它提供了一些内置功能,例如,

add(element):在尾部添加一个元素。

addFirst(element):向头部添加一个元素。

addLast(element):在尾部添加一个元素。

offer(element):在尾部添加一个元素并返回一个布尔值来说明插入是否成功。

offerFirst(element):向头部添加一个元素并返回一个布尔值 说明插入是否成功。

offerLast(element):在尾部添加一个元素并返回一个布尔值来说明插入是否成功。

iterator(): 为这个双端队列返回一个迭代器。

descendingIterator():返回一个与此双端队列相反顺序的迭代器。

push(element):向头部添加一个元素。

pop(element):从头部移除一个元素并返回它。

removeFirst():移除头部元素。

removeLast():移除尾部的元素。

有关双端队列的更多信息:https://examples.javacodegeeks.com/core-java/util/deque-util/java-util-deque-example/

【讨论】:

    【解决方案3】:

    鉴于您在示例中显示了put(k, v),我假设您也将此数据结构用于基于键的查找,因此简单的队列并不理想。

    在这种情况下,您可以使用由Collection.synchronized 包裹的LikedHashMap,并实现removeEldest 方法。下面的示例没有显示多个线程,但应该可以很好地处理多个线程。为了对要移除的项目执行您自己的计算,您在BoundedMap 的子类中实现entryRemoved 方法:

    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.Collections;
    
    class BoundedMap<K, V> extends LinkedHashMap<K, V> {
      private final int capacity;
    
      public BoundedMap(int capacity) {
        this.capacity = capacity;
      }
    
      @Override
      protected boolean removeEldestEntry(Map.Entry<K, V> eldest){
        if(size() > capacity) {
          remove(eldest.getKey());
          entryRemoved(eldest);
        }
        return false;
      }
    
      public void entryRemoved(Map.Entry<K,V> entry) {
      }
    }
    
    public class Test {
      public static void main(String[] args) {
        Map<Integer, Integer> m = Collections.synchronizedMap(
          new BoundedMap<Integer, Integer>(5) {
            @Override
            public void entryRemoved(Map.Entry entry) {
              System.out.println(entry);
            }
        });
        System.out.println("--- adding");
        for (int i=0; i<8; i++) {
          m.put(i, -i);
        }
        System.out.println("-- iterating");
        for(Map.Entry<Integer, Integer> entry : m.entrySet()) {
          System.out.println(entry);
        }
      }
    }
    

    【讨论】:

      【解决方案4】:

      您可以通过覆盖removeEldestEntry()java.util.LinkedHashMap 的简单子类来执行此操作,如其Javadoc 中所述。

      【讨论】:

        猜你喜欢
        • 2021-03-04
        • 2010-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多