ฉันต้องการคิวที่มีขนาดคงที่ เมื่อฉันเพิ่มองค์ประกอบและคิวเต็มมันควรจะลบองค์ประกอบที่เก่าที่สุดโดยอัตโนมัติ
มีการนำไปใช้งานสำหรับสิ่งนี้ใน Java หรือไม่?
ฉันต้องการคิวที่มีขนาดคงที่ เมื่อฉันเพิ่มองค์ประกอบและคิวเต็มมันควรจะลบองค์ประกอบที่เก่าที่สุดโดยอัตโนมัติ
มีการนำไปใช้งานสำหรับสิ่งนี้ใน Java หรือไม่?
คำตอบ:
ไม่มีการนำไปใช้งานในภาษา Java และรันไทม์ คิวทั้งหมดขยายAbstractQueueและเอกสารระบุชัดเจนว่าการเพิ่มองค์ประกอบในคิวแบบเต็มจะลงท้ายด้วยข้อยกเว้นเสมอ จะเป็นการดีที่สุด (และค่อนข้างง่าย) ที่จะรวม Queue ไว้ในคลาสของคุณเองเพื่อให้มีฟังก์ชันที่คุณต้องการ
อีกครั้งเนื่องจากคิวทั้งหมดเป็นลูกของ AbstractQueue เพียงใช้สิ่งนั้นเป็นประเภทข้อมูลภายในของคุณและคุณควรมีการใช้งานที่ยืดหยุ่นในแทบจะไม่มีเวลา :-)
UPDATE:
ตามที่ระบุไว้ด้านล่างมีการใช้งานแบบเปิดอยู่สองแบบ (คำตอบนี้ค่อนข้างเก่าคน!) ดูรายละเอียดคำตอบนี้
collection.deque
กับไฟล์maxlen
.
ที่จริงแล้วLinkedHashMapทำในสิ่งที่คุณต้องการ คุณต้องลบล้างremoveEldestEntry
เมธอด
ตัวอย่างสำหรับคิวที่มีองค์ประกอบสูงสุด 10 รายการ:
queue = new LinkedHashMap<Integer, String>()
{
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest)
{
return this.size() > 10;
}
};
ถ้า "removeEldestEntry" คืนค่าเป็นจริงรายการที่เก่าที่สุดจะถูกลบออกจากแผนที่
จากคำถามที่ซ้ำกันของฉันเองกับคำตอบที่ถูกต้องนี้ฉันได้เรียนรู้สองข้อ:
ฉันใช้ประโยชน์จากฝรั่งEvictingQueue
ได้ผลดี
เพื่อสร้างอินสแตนซ์การEvictingQueue
โทรด้วยวิธีโรงงานแบบคงที่create
และระบุขนาดสูงสุดของคุณ
EvictingQueue< Person > people = com.google.common.collect.EvictingQueue.create( 100 ) ; // Set maximum size to 100.
CircularFifoQueue
ลิงค์ตายใช้แทนcommons.apache.org/proper/commons-collections/apidocs/org/…
ฉันเพิ่งใช้คิวขนาดคงที่ด้วยวิธีนี้:
public class LimitedSizeQueue<K> extends ArrayList<K> {
private int maxSize;
public LimitedSizeQueue(int size){
this.maxSize = size;
}
public boolean add(K k){
boolean r = super.add(k);
if (size() > maxSize){
removeRange(0, size() - maxSize);
}
return r;
}
public K getYoungest() {
return get(size() - 1);
}
public K getOldest() {
return get(0);
}
}
removeRange(0, size() - maxSize)
นี่คือสิ่งที่ฉันทำกับการQueue
ห่อด้วยLinkedList
ขนาดคงที่ซึ่งฉันให้ไว้ที่นี่คือ 2;
public static Queue<String> pageQueue;
pageQueue = new LinkedList<String>(){
private static final long serialVersionUID = -6707803882461262867L;
public boolean add(String object) {
boolean result;
if(this.size() < 2)
result = super.add(object);
else
{
super.removeFirst();
result = super.add(object);
}
return result;
}
};
....
TMarket.pageQueue.add("ScreenOne");
....
TMarket.pageQueue.add("ScreenTwo");
.....
คลาสนี้ทำงานโดยใช้องค์ประกอบแทนการสืบทอด (คำตอบอื่น ๆ ที่นี่) ซึ่งจะลบความเป็นไปได้ของผลข้างเคียงบางอย่าง (ตามที่ Josh Bloch กล่าวไว้ใน Essential Java) การตัดแต่งของ LinkedList พื้นฐานเกิดขึ้นกับวิธีการ add, addAll และ offer
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
public class LimitedQueue<T> implements Queue<T>, Iterable<T> {
private final int limit;
private final LinkedList<T> list = new LinkedList<T>();
public LimitedQueue(int limit) {
this.limit = limit;
}
private boolean trim() {
boolean changed = list.size() > limit;
while (list.size() > limit) {
list.remove();
}
return changed;
}
@Override
public boolean add(T o) {
boolean changed = list.add(o);
boolean trimmed = trim();
return changed || trimmed;
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object o) {
return list.contains(o);
}
@Override
public Iterator<T> iterator() {
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
@Override
public boolean remove(Object o) {
return list.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends T> c) {
boolean changed = list.addAll(c);
boolean trimmed = trim();
return changed || trimmed;
}
@Override
public boolean removeAll(Collection<?> c) {
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return list.retainAll(c);
}
@Override
public void clear() {
list.clear();
}
@Override
public boolean offer(T e) {
boolean changed = list.offer(e);
boolean trimmed = trim();
return changed || trimmed;
}
@Override
public T remove() {
return list.remove();
}
@Override
public T poll() {
return list.poll();
}
@Override
public T element() {
return list.element();
}
@Override
public T peek() {
return list.peek();
}
}
public class CircularQueue<E> extends LinkedList<E> {
private int capacity = 10;
public CircularQueue(int capacity){
this.capacity = capacity;
}
@Override
public boolean add(E e) {
if(size() >= capacity)
removeFirst();
return super.add(e);
}
}
การใช้งานและผลการทดสอบ:
public static void main(String[] args) {
CircularQueue<String> queue = new CircularQueue<>(3);
queue.add("a");
queue.add("b");
queue.add("c");
System.out.println(queue.toString()); //[a, b, c]
String first = queue.pollFirst(); //a
System.out.println(queue.toString()); //[b,c]
queue.add("d");
queue.add("e");
queue.add("f");
System.out.println(queue.toString()); //[d, e, f]
}
ดูเหมือนรายการธรรมดาที่วิธีการเพิ่มมีส่วนย่อยเพิ่มเติมซึ่งจะตัดทอนรายการหากยาวเกินไป
หากง่ายเกินไปคุณอาจต้องแก้ไขคำอธิบายปัญหาของคุณ
นอกจากนี้โปรดดูคำถาม SO นี้หรือArrayBlockingQueue (โปรดระวังเกี่ยวกับการบล็อกซึ่งอาจไม่ต้องการในกรณีของคุณ)
ยังไม่ชัดเจนว่าคุณมีข้อกำหนดใดบ้างที่ทำให้คุณต้องถามคำถามนี้ หากคุณต้องการโครงสร้างข้อมูลขนาดคงที่คุณอาจต้องการดูนโยบายการแคชที่แตกต่างกัน อย่างไรก็ตามเนื่องจากคุณมีคิวการคาดเดาที่ดีที่สุดของฉันคือคุณกำลังมองหาฟังก์ชันการทำงานของเราเตอร์บางประเภท ในกรณีนั้นฉันจะใช้วงแหวนบัฟเฟอร์: อาร์เรย์ที่มีดัชนีตัวแรกและตัวสุดท้าย เมื่อใดก็ตามที่มีการเพิ่มองค์ประกอบคุณเพียงแค่เพิ่มดัชนีองค์ประกอบสุดท้ายและเมื่อนำองค์ประกอบออกให้เพิ่มดัชนีองค์ประกอบแรก ในทั้งสองกรณีการเพิ่มจะดำเนินการโมดูโลขนาดอาร์เรย์และตรวจสอบให้แน่ใจว่าได้เพิ่มดัชนีอื่นเมื่อจำเป็นนั่นคือเมื่อคิวเต็มหรือว่างเปล่า
นอกจากนี้หากเป็นแอปพลิเคชันประเภทเราเตอร์คุณอาจต้องการทดลองใช้อัลกอริทึมเช่น Random Early Dropping (RED) ซึ่งจะดร็อปองค์ประกอบจากคิวแบบสุ่มก่อนที่มันจะเต็ม ในบางกรณีพบว่า RED มีประสิทธิภาพโดยรวมดีกว่าวิธีง่ายๆในการปล่อยให้คิวเต็มก่อนที่จะวาง
จริงๆแล้วคุณสามารถเขียน im ของคุณเองตาม LinkedList มันค่อนข้างตรงไปตรงมาเพียงแค่ลบล้างวิธีการเพิ่มและให้เจ้าหน้าที่
ฉันคิดว่าคำตอบที่ตรงกันที่สุดมาจากคำถามอื่น ๆนี้
Apache commons คอลเลกชัน 4 มีCircularFifoQueueซึ่งเป็นสิ่งที่คุณกำลังมองหา อ้างถึง javadoc:
CircularFifoQueue เป็นคิวแรกเข้าก่อนออกโดยมีขนาดคงที่ซึ่งจะแทนที่องค์ประกอบที่เก่าที่สุดหากเต็ม
วิธีง่ายๆด้านล่างคือคิวของ "String"
LinkedHashMap<Integer, String> queue;
int queueKeysCounter;
queue.put(queueKeysCounter++, "My String");
queueKeysCounter %= QUEUE_SIZE;
โปรดทราบว่าสิ่งนี้จะไม่คงลำดับของรายการในคิว แต่จะแทนที่รายการที่เก่าที่สุด
ตามคำแนะนำใน OOPs ว่าเราควรชอบComposition มากกว่า Inheritance
นี่คือทางออกของฉันโดยคำนึงถึงสิ่งนั้น
package com.choiceview;
import java.util.ArrayDeque;
class Ideone {
public static void main(String[] args) {
LimitedArrayDeque<Integer> q = new LimitedArrayDeque<>(3);
q.add(1);
q.add(2);
q.add(3);
System.out.println(q);
q.add(4);
// First entry ie 1 got pushed out
System.out.println(q);
}
}
class LimitedArrayDeque<T> {
private int maxSize;
private ArrayDeque<T> queue;
private LimitedArrayDeque() {
}
public LimitedArrayDeque(int maxSize) {
this.maxSize = maxSize;
queue = new ArrayDeque<T>(maxSize);
}
public void add(T t) {
if (queue.size() == maxSize) {
queue.removeFirst();
}
queue.add(t);
}
public boolean remove(T t) {
return queue.remove(t);
}
public boolean contains(T t) {
return queue.contains(t);
}
@Override
public String toString() {
return queue.toString();
}
}