在10分钟的窗口期间存储对象

时间:2013-05-17 10:47:43

标签: java

我正在努力解决面试时提出的问题。我在面试时无法解决这个问题,所以我要求你帮忙知道。

问题是:

使用带整数的方法编写一个类,并返回在过去十分钟内调用该方法的最大值的整数。

根据我的理解,我必须存储过去10分钟调用该方法的所有值。值应存储在高效的数据结构中,因为此方法可能每秒调用几次。

对于哪种数据结构对此更有效,您有什么建议吗? 此外,由于这是一个时间滚动窗口,如何清除过期的值?

根据所使用的数据结构,获取最大值的最佳方法是什么?

我有一些基本代码:

    private final static ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor(); 

    private static List<Integer> values = new ArrayList<Integer>();


    public int method(final int value){
        values.add(value);

         // Task to remove the key-value pair     
        Runnable task = new Runnable() {     
            @Override     
            public void run() {     
                values.remove(value);
            }     
        };     

        // Schedule the task to run after the delay  
        EXECUTOR_SERVICE.schedule(task, 60, TimeUnit.SECONDS);  


        //TODO get the max value
        return 1;
    }

4 个答案:

答案 0 :(得分:5)

Entry类定义为(时间戳)time和(int)value;

使用LinkedList<Entry>保持滑动窗口:在结尾插入,在开头删除过期。使用TreeMap<Integer, ArrayList<Entry>>保持O(1)查找最大值(使用.lastEntry()获取最大值)。我们的想法是按value排序并保留具有该值的所有条目的列表;对于添加或删除的每个条目,必须更新树(在O(log(N))中一次。

不要使用调度程序;每当新请求到达时(或者请求“最大”)进行清理,它会更便宜,更快。

答案 1 :(得分:2)

这实际上可以在摊还的常数时间内完成:

values := deque of timestamped values

function prune()
    while values.front is older than 10 minutes
        values.pop_front()

function add(v)
    while values is not empty and v is greater than values.back
        values.pop_back()
    values.push_back(v)

function getMax()
    prune()
    return values.front()

使用观察结果以递减的顺序存储值,当您获得新值时,您可能会忘记较小的旧值。

答案 2 :(得分:1)

关于您的代码的一些评论:

  • values.remove(value);正在调用remove(int index)而不是remove(Object o)。因此它将删除位于index =值的值。您可以使用values.remove(Integer.valueOf(value));代替
  • 您没有提供返回最大值的部分 - 但是使用列表您需要浏览整个列表以找到最大值,因此该方法将是O(n),这不是非常有效。
  • 使用调度程序在1分钟后删除值的想法很好

备选方案1:

  • 创建一个持有者类:

    class Holder {
        private static AtomicInteger staticVersionCounter = new AtomicInteger();
        private int value;
        private int version;
    }
    
  • 当您收到新的int时,请创建new Holder(value, staticVersionCounter.incrementAndGet());并将其放在TreeSet中,并使用自定义比较器按升序值和版本进行排序(以确保相同值不会被覆盖)

  • 可以使用TreeSet#last()方法
  • 有效地检索最大值
  • 您可以让调度程序在到期后删除条目

答案 3 :(得分:0)

我会使用LinkedList<IntegerTimePair>这样做,你可以很容易地获得第一个元素,但也可以轻松地获取(和删除)元素(最旧的元素) - 一个LinkedList对此有好处,因为像ArrayList一样没有变化。我的第一个猜测是在某个时间间隔内检查,从列表的后面开始,删除所有超过十分钟的时间。

我唯一的问题是Java的LinkedList是否是双向的并且保持开头和结尾。如果没有,使用存储的尾指针写一个像removeFromEnd()这样的操作。

获取最大值可以使用列表扫描完成,或者通过保持最大值并仅在更高的值插入新值时更新,或者在删除时扫描最新的最大值(如果删除)最大值)。