Java 具有生存时间的消息队列

Java 具有生存时间的消息队列,java,collections,message-queue,java.util.concurrent,Java,Collections,Message Queue,Java.util.concurrent,我查找一个队列,该队列在特定时间(即10秒)内最多存储N个元素,或者如果已满,则应处理最旧的值 我在Apache集合中发现了一个类似的队列(CircularFifoQueue),它忽略了生存时间这一方面。对于我的小项目来说,一个完整的消息代理似乎是一项开销 你介意给我一个我应该如何实施的提示吗?在轮询元素时是否要过滤掉旧值?有一个名为的类,它有一个特殊的方法来删除过时的数据。从文件中: 受保护的布尔重构(Map.Entry最早) 如果此映射应删除其最早的条目,则返回true 每当向列表(队列)中

我查找一个队列,该队列在特定时间(即10秒)内最多存储
N
个元素,或者如果已满,则应处理最旧的值

我在Apache集合中发现了一个类似的队列(
CircularFifoQueue
),它忽略了生存时间这一方面。对于我的小项目来说,一个完整的消息代理似乎是一项开销

你介意给我一个我应该如何实施的提示吗?在轮询元素时是否要过滤掉旧值?

有一个名为的类,它有一个特殊的方法来删除过时的数据。从文件中:

受保护的布尔重构(Map.Entry最早)
如果此映射应删除其最早的条目,则返回true

每当向列表(队列)中添加任何内容时,都会调用方法
removeEldestEntry
。如果返回
true
,则删除最老的条目以为新条目腾出空间,否则不删除任何内容。您可以添加自己的实现,该实现检查最老条目上的时间戳,如果时间戳早于到期阈值(例如10秒),则返回
true
。因此,您的实现可能如下所示:

protected boolean removeEldestEntry(Map.Entry eldest) {
    long currTimeMillis = System.currentTimeMillis();
    long entryTimeMillis = eldest.getValue().getTimeCreation();

    return (currTimeMillis - entryTimeMillis) > (1000*10*60); 
}
我认为这是你的解决办法。它有一个
removeEldest()
方法,每当在映射中放入条目时都会调用该方法。您可以覆盖它以指示是否应删除最旧的条目

JavaDoc提供了一个很好的例子:

 private static final int MAX_ENTRIES = 100;

 protected boolean removeEldestEntry(Map.Entry eldest) {
    return size() > MAX_ENTRIES;
 }
如果地图上有100多个条目,则会删除最老的条目


在10秒后主动移除物品需要一个单独的线程来检查年龄并移除旧物品。从您的描述来看,我猜这不是您想要的。

我过去常常遵循队列实现。该代码主要基于Apaches,并且只经过弱测试。此外,该实现是非线程安全的不可序列化的

如果发现错误,请留下评论

import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.concurrent.TimeUnit;

/**
 * TimedQueue is a first-in first-out queue with a fixed size that
 * replaces its oldest element if full.
 * <p>
 * The removal order of a {@link TimedQueue} is based on the
 * insertion order; elements are removed in the same order in which they
 * were added.  The iteration order is the same as the removal order.
 * <p>
 * The {@link #add(Object)}, {@link #remove()}, {@link #peek()}, {@link #poll},
 * {@link #offer(Object)} operations all perform in constant time.
 * All other operations perform in linear time or worse.
 * <p>
 * This queue prevents null objects from being added and it is not thread-safe and not serializable.
 * 
 * The majority of this source code was copied from Apaches {@link CircularFifoQueue}: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/queue/CircularFifoQueue.html
 *
 * @version 1.0
 */
public class TimedQueue<E> extends AbstractCollection<E>
implements Queue<E> {

/** Underlying storage array. */
private Item<E>[] elements;

/** Array index of first (oldest) queue element. */
private int start = 0;

/**
 * Index mod maxElements of the array position following the last queue
 * element.  Queue elements start at elements[start] and "wrap around"
 * elements[maxElements-1], ending at elements[decrement(end)].
 * For example, elements = {c,a,b}, start=1, end=1 corresponds to
 * the queue [a,b,c].
 */
private transient int end = 0;

/** Flag to indicate if the queue is currently full. */
private transient boolean full = false;

/** Capacity of the queue. */
private final int maxElements;

private TimeUnit unit;
private int delay;

/**
 * Constructor that creates a queue with the default size of 32.
 */
public TimedQueue() {
    this(32);
}

/**
 * Constructor that creates a queue with the specified size.
 *
 * @param size  the size of the queue (cannot be changed)
 * @throws IllegalArgumentException  if the size is &lt; 1
 */
public TimedQueue(final int size) {
    this(size, 3, TimeUnit.SECONDS);
}

@SuppressWarnings("unchecked")
public TimedQueue(final int size, int delay, TimeUnit unit) {
    if (size <= 0) {
        throw new IllegalArgumentException("The size must be greater than 0");
    }
    elements = new Item[size];
    maxElements = elements.length;
    this.unit = unit;
    this.delay = delay;
}

/**
 * Constructor that creates a queue from the specified collection.
 * The collection size also sets the queue size.
 *
 * @param coll  the collection to copy into the queue, may not be null
 * @throws NullPointerException if the collection is null
 */
public TimedQueue(final Collection<? extends E> coll) {
    this(coll.size());
    addAll(coll);
}

/**
 * Returns the number of elements stored in the queue.
 *
 * @return this queue's size
 */
@Override
public int size() {
    int size = 0;

    for(int i = 0; i < elements.length; i++) {
        if(validElement(i) != null) {
            size++;
        }
    }

    return size;
}

/**
 * Returns true if this queue is empty; false otherwise.
 *
 * @return true if this queue is empty
 */
@Override
public boolean isEmpty() {
    return size() == 0;
}

private boolean isAtFullCapacity() {
    return size() == maxElements;
}

/**
 * Clears this queue.
 */
@Override
public void clear() {
    full = false;
    start = 0;
    end = 0;
    Arrays.fill(elements, null);
}

/**
 * Adds the given element to this queue. If the queue is full, the least recently added
 * element is discarded so that a new element can be inserted.
 *
 * @param element  the element to add
 * @return true, always
 * @throws NullPointerException  if the given element is null
 */
@Override
public boolean add(final E element) {
    if (null == element) {
        throw new NullPointerException("Attempted to add null object to queue");
    }

    if (isAtFullCapacity()) {
        remove();
    }

    elements[end++] = new Item<E>(element);

    if (end >= maxElements) {
        end = 0;
    }

    if (end == start) {
        full = true;
    }

    return true;
}

/**
 * Returns the element at the specified position in this queue.
 *
 * @param index the position of the element in the queue
 * @return the element at position {@code index}
 * @throws NoSuchElementException if the requested position is outside the range [0, size)
 */
public E get(final int index) {
    final int sz = size();
    if (sz == 0) {
        throw new NoSuchElementException(
                String.format("The specified index (%1$d) is outside the available range because the queue is empty.", Integer.valueOf(index)));
    }
    if (index < 0 || index >= sz) {
        throw new NoSuchElementException(
                String.format("The specified index (%1$d) is outside the available range [0, %2$d]",
                              Integer.valueOf(index), Integer.valueOf(sz-1)));
    }

    final int idx = (start + index) % maxElements;
    return validElement(idx);
}

private E validElement(int idx) {
    if(elements[idx] == null){
        return null;
    }
    long diff = System.currentTimeMillis() - elements[idx].getCreationTime();

    if(diff < unit.toMillis(delay)) {
        return (E) elements[idx].getValue();
    } else {
        elements[idx] = null;
        return null;
    }
}

//-----------------------------------------------------------------------

/**
 * Adds the given element to this queue. If the queue is full, the least recently added
 * element is discarded so that a new element can be inserted.
 *
 * @param element  the element to add
 * @return true, always
 * @throws NullPointerException  if the given element is null
 */
public boolean offer(E element) {
    return add(element);
}

public E poll() {
    if (isEmpty()) {
        return null;
    }
    return remove();
}

public E element() {
    if (isEmpty()) {
        throw new NoSuchElementException("queue is empty");
    }
    return peek();
}

public E peek() {
    if (isEmpty()) {
        return null;
    }
    return (E) elements[start].getValue();
}

public E remove() {
    if (isEmpty()) {
        throw new NoSuchElementException("queue is empty");
    }

    final E element = validElement(start);
    if (null != element) {
        elements[start++] = null;

        if (start >= maxElements) {
            start = 0;
        }
        full = false;
    }
    return element;
}

/**
 * Increments the internal index.
 *
 * @param index  the index to increment
 * @return the updated index
 */
private int increment(int index) {
    index++;
    if (index >= maxElements) {
        index = 0;
    }
    return index;
}

/**
 * Decrements the internal index.
 *
 * @param index  the index to decrement
 * @return the updated index
 */
private int decrement(int index) {
    index--;
    if (index < 0) {
        index = maxElements - 1;
    }
    return index;
}

/**
 * Returns an iterator over this queue's elements.
 *
 * @return an iterator over this queue's elements
 */
@Override
public Iterator<E> iterator() {
    return new Iterator<E>() {

        private int index = start;
        private int lastReturnedIndex = -1;
        private boolean isFirst = full;

        public boolean hasNext() {
            return (isFirst || index != end) && size() > 0;
        }

        public E next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            isFirst = false;
            lastReturnedIndex = index;
            index = increment(index);
            if(validElement(lastReturnedIndex) == null) {
                return next();
            } else {
                return validElement(lastReturnedIndex);
            }
        }

        public void remove() {
            if (lastReturnedIndex == -1) {
                throw new IllegalStateException();
            }

            // First element can be removed quickly
            if (lastReturnedIndex == start) {
                TimedQueue.this.remove();
                lastReturnedIndex = -1;
                return;
            }

            int pos = lastReturnedIndex + 1;
            if (start < lastReturnedIndex && pos < end) {
                // shift in one part
                System.arraycopy(elements, pos, elements, lastReturnedIndex, end - pos);
            } else {
                // Other elements require us to shift the subsequent elements
                while (pos != end) {
                    if (pos >= maxElements) {
                        elements[pos - 1] = elements[0];
                        pos = 0;
                    } else {
                        elements[decrement(pos)] = elements[pos];
                        pos = increment(pos);
                    }
                }
            }

            lastReturnedIndex = -1;
            end = decrement(end);
            elements[end] = null;
            full = false;
            index = decrement(index);
        }

    };
}

private static final class Item<E> {
    private long creationTime;
    private E in;

    public Item(E in) {
        this.in = in;
        creationTime = System.currentTimeMillis();
    }

    public E getValue() {
        return in;
    }

    public long getCreationTime() {
        return creationTime;
    }
}
}
import java.util.AbstractCollection;
导入java.util.array;
导入java.util.Collection;
导入java.util.Iterator;
导入java.util.NoSuchElementException;
导入java.util.Queue;
导入java.util.concurrent.TimeUnit;
/**
*TimedQueue是一个具有固定大小的先进先出队列
*如果已满,则替换其最早的元素。
*
*{@link TimedQueue}的删除顺序基于
*插入顺序;删除元素的顺序与删除元素的顺序相同
*增加了。迭代顺序与删除顺序相同。
*
*{@link#add(Object)}、{@link#remove()}、{@link#peek()}、{@link#poll},
*{@link#offer(Object)}操作都在固定时间内执行。
*所有其他操作都以线性时间或更差时间执行。
*
*此队列防止添加空对象,并且它不是线程安全的,也不可序列化。
* 
*此源代码的大部分是从Apaches{@link CircularFifoQueue}复制的:http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/queue/CircularFifoQueue.html
*
*@version 1.0
*/
公共类TimedQueue扩展了AbstractCollection
实现队列{
/**底层存储阵列*/
私有项[]元素;
/**第一个(最早的)队列元素的数组索引*/
私有int start=0;
/**
*索引最后一个队列后面的数组位置的mod maxElements
*元素。队列元素从元素[start]和“环绕”开始
*元素[maxElements-1],结束于元素[Decreation(end)]。
*例如,元素={c,a,b},start=1,end=1对应于
*队列[a,b,c]。
*/
专用瞬态int end=0;
/**指示队列当前是否已满的标志*/
private transient boolean full=false;
/**队列的容量*/
私有final int maxElements;
私人计时单位;
私有整数延迟;
/**
*用于创建默认大小为32的队列的构造函数。
*/
公共时间队列(){
这(32);
}
/**
*用于创建具有指定大小的队列的构造函数。
*
*@param size队列的大小(无法更改)
*@如果大小为1,则抛出IllegalArgumentException
*/
公共TimedQueue(最终整数大小){
这(大小,3,时间单位。秒);
}
@抑制警告(“未选中”)
公共TimedQueue(最终整数大小、整数延迟、时间单位){

如果(大小)此答案应为注释。这看起来是个好主意。但是,我想删除超过10秒的项目。是否已经实现了这种“基于时间的队列”?我会检查并通知你!这个答案很好!@Markus这对你来说是个问题吗?旧的值最终都会被删除。我相信我的答案会很好地满足你的需要。如果你想限制队列的大小,并使用我的解决方案在新条目出现时剔除任何过时条目。我相信你可以使当前的界面e为您的用例工作。@Markus我支持任何能让您解决问题的方法。如果我能提供进一步帮助,请告诉我。