web-dev-qa-db-fra.com

Ring Buffer dans Java

J'ai une série chronologique en continu, ce qui m'intéresse de conserver les 4 derniers éléments, ce qui signifie que je veux pouvoir faire apparaître le premier et l'ajouter à la fin. Ce dont j'ai essentiellement besoin, c'est d'un tampon en anneau .

Laquelle Java Collection est la meilleure solution? Vector?

73
Truba

Considérez CircularFifoBuffer à partir d'Apache Common.Collections . Contrairement à file d'attente , vous n'avez pas à conserver la taille limitée de la collection sous-jacente et à l'envelopper une fois que vous avez atteint la limite.

Buffer buf = new CircularFifoBuffer(4);
buf.add("A");
buf.add("B");
buf.add("C");
buf.add("D"); //ABCD
buf.add("E"); //BCDE

CircularFifoBuffer le fera pour vous en raison des propriétés suivantes:

  • CircularFifoBuffer est un tampon premier entré premier sorti avec une taille fixe qui remplace son élément le plus ancien s'il est plein.
  • L'ordre de suppression d'un CircularFifoBuffer est basé sur l'ordre d'insertion. les éléments sont supprimés dans le même ordre dans lequel ils ont été ajoutés. L'ordre d'itération est identique à l'ordre de suppression.
  • Les opérations add (Object), BoundedFifoBuffer.remove () et BoundedFifoBuffer.get () sont toutes exécutées en temps constant . Toutes les autres opérations fonctionnent en temps linéaire ou pire.

Cependant, vous devez également tenir compte de ses limites. Par exemple, vous ne pouvez pas ajouter de séries temporelles manquantes à cette collection car elle n'autorise pas les valeurs NULL.

REMARQUE: lors de l'utilisation de Collections communes (4. *), vous devez utiliser la file d'attente. Comme ça:

Queue buf = new CircularFifoQueue(4);
91
Andrey Taptunov

Depuis Guava 15.0 (sortie en septembre 2013), il y a EvictingQueue :

Une file d'attente non bloquante qui supprime automatiquement les éléments de la tête de la file d'attente lorsque vous essayez d'ajouter de nouveaux éléments à la file d'attente, qui est pleine. Une file d'attente d'expulsion doit être configurée avec une taille maximale. Chaque fois qu'un élément est ajouté à une file d'attente complète, la file d'attente supprime automatiquement son élément de tête. Cela diffère des files d'attente limitées conventionnelles, qui bloquent ou rejettent de nouveaux éléments lorsqu'elles sont pleines.

Cette classe n'est pas thread-safe et n'accepte pas les éléments null.

Exemple d'utilisation:

EvictingQueue<String> queue = EvictingQueue.create(2);
queue.add("a");
queue.add("b");
queue.add("c");
queue.add("d");
System.out.print(queue); //outputs [c, d]
45

Depuis Java 1.6, il y a ArrayDeque, qui implémente Queue et semble être plus rapide et plus efficace en termes de mémoire qu'un LinkedList et doesn ' t a la surcharge de synchronisation de thread de ArrayBlockingQueue: à partir des documents de l'API: "Cette classe est probablement plus rapide que Stack lorsqu'elle est utilisée en tant que pile, et plus rapide que LinkedList lorsqu'elle est utilisée en tant que file d'attente. "

final Queue<Object> q = new ArrayDeque<Object>();
q.add(new Object()); //insert element
q.poll(); //remove element
15
T. Baum

Si tu as besoin

  • O (1) insertion et retrait
  • O (1) indexation sur les éléments intérieurs
  • accès à partir d'un seul thread
  • type d'élément générique

alors vous pouvez utiliser this CircularArrayList pour Java de cette manière (par exemple):

CircularArrayList<String> buf = new CircularArrayList<String>(4);

buf.add("A");
buf.add("B");
buf.add("C");
buf.add("D"); // ABCD

String pop = buf.remove(0); // A <- BCD
buf.add("E"); // BCDE

String interiorElement = buf.get(i);

Toutes ces méthodes fonctionnent en O (1).

11
Museful

J’ai eu le même problème il ya un certain temps et j’ai été déçu car je ne trouvais pas de solution répondant à mes besoins et j’écrivais ma propre classe. Honnêtement, j'avais trouvé du code à l'époque, mais même si ce n'était pas ce que je cherchais, je l'ai adapté et je le partage maintenant, tout comme l'auteur de ce code.

EDIT: Ceci est le code original (bien que légèrement différent): CircularArrayList for Java

Je n'ai pas le lien de la source car c'était il y a longtemps, mais voici le code:

import Java.util.AbstractList;
import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.List;
import Java.util.RandomAccess;

public class CircularArrayList<E> extends AbstractList<E> implements RandomAccess {

private final int n; // buffer length
private final List<E> buf; // a List implementing RandomAccess
private int leader = 0;
private int size = 0;


public CircularArrayList(int capacity) {
    n = capacity + 1;
    buf = new ArrayList<E>(Collections.nCopies(n, (E) null));
}

public int capacity() {
    return n - 1;
}

private int wrapIndex(int i) {
    int m = i % n;
    if (m < 0) { // modulus can be negative
        m += n;
    }
    return m;
}

@Override
public int size() {
    return this.size;
}

@Override
public E get(int i) {
    if (i < 0 || i >= n-1) throw new IndexOutOfBoundsException();

    if(i > size()) throw new NullPointerException("Index is greater than size.");

    return buf.get(wrapIndex(leader + i));
}

@Override
public E set(int i, E e) {
    if (i < 0 || i >= n-1) {
        throw new IndexOutOfBoundsException();
    }
    if(i == size()) // assume leader's position as invalid (should use insert(e))
        throw new IndexOutOfBoundsException("The size of the list is " + size() + " while the index was " + i
                +". Please use insert(e) method to fill the list.");
    return buf.set(wrapIndex(leader - size + i), e);
}

public void insert(E e)
{
    int s = size();     
    buf.set(wrapIndex(leader), e);
    leader = wrapIndex(++leader);
    buf.set(leader, null);
    if(s == n-1)
        return; // we have replaced the eldest element.
    this.size++;

}

@Override
public void clear()
{
    int cnt = wrapIndex(leader-size());
    for(; cnt != leader; cnt = wrapIndex(++cnt))
        this.buf.set(cnt, null);
    this.size = 0;      
}

public E removeOldest() {
    int i = wrapIndex(leader+1);

    for(;;i = wrapIndex(++i)) {
        if(buf.get(i) != null) break;
        if(i == leader)
            throw new IllegalStateException("Cannot remove element."
                    + " CircularArrayList is empty.");
    }

    this.size--;
    return buf.set(i, null);
}

@Override
public String toString()
{
    int i = wrapIndex(leader - size());
    StringBuilder str = new StringBuilder(size());

    for(; i != leader; i = wrapIndex(++i)){
        str.append(buf.get(i));
    }
    return str.toString();
}

public E getOldest(){
    int i = wrapIndex(leader+1);

    for(;;i = wrapIndex(++i)) {
        if(buf.get(i) != null) break;
        if(i == leader)
            throw new IllegalStateException("Cannot remove element."
                    + " CircularArrayList is empty.");
    }

    return buf.get(i);
}

public E getNewest(){
    int i = wrapIndex(leader-1);
    if(buf.get(i) == null)
        throw new IndexOutOfBoundsException("Error while retrieving the newest element. The Circular Array list is empty.");
    return buf.get(i);
}
}
5
vgfeit

Un projet très intéressant est perturbateur. Il a un ringbuffer et est utilisé à partir de ce que je sais dans les applications financières.

Voir ici: code de ringbuffer

J'ai vérifié les fichiers EvictingQueue et ArrayDeque de Guava.

ArrayDeque ne limite pas la croissance si elle est pleine, elle doublera de taille et ne se comporte donc pas exactement comme un tampon.

EvictingQueue fait ce qu'il promet, mais utilise en interne un Deque pour stocker des objets et ne limite que la mémoire.

Par conséquent, si vous tenez à ce que la mémoire soit limitée, ArrayDeque ne remplit pas votre promesse. Si vous vous souciez du nombre d’objets, EvictingQueue utilise une composition interne (taille d’objet supérieure).

Un simple et efficace en mémoire peut être volé à jmonkeyengine . copie conforme

import Java.util.Iterator;
import Java.util.NoSuchElementException;

public class RingBuffer<T> implements Iterable<T> {

  private T[] buffer;          // queue elements
  private int count = 0;          // number of elements on queue
  private int indexOut = 0;       // index of first element of queue
  private int indexIn = 0;       // index of next available slot

  // cast needed since no generic array creation in Java
  public RingBuffer(int capacity) {
    buffer = (T[]) new Object[capacity];
  }

  public boolean isEmpty() {
    return count == 0;
  }

  public int size() {
    return count;
  }

  public void Push(T item) {
    if (count == buffer.length) {
        throw new RuntimeException("Ring buffer overflow");
    }
    buffer[indexIn] = item;
    indexIn = (indexIn + 1) % buffer.length;     // wrap-around
    count++;
  }

  public T pop() {
    if (isEmpty()) {
        throw new RuntimeException("Ring buffer underflow");
    }
    T item = buffer[indexOut];
    buffer[indexOut] = null;                  // to help with garbage collection
    count--;
    indexOut = (indexOut + 1) % buffer.length; // wrap-around
    return item;
  }

  public Iterator<T> iterator() {
    return new RingBufferIterator();
  }

  // an iterator, doesn't implement remove() since it's optional
  private class RingBufferIterator implements Iterator<T> {

    private int i = 0;

    public boolean hasNext() {
        return i < count;
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }

    public T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return buffer[i++];
    }
  }
}
2
Alex

Aucun des exemples précédemment cités ne répondait complètement à mes besoins, j'ai donc écrit ma propre file d'attente qui permet les fonctionnalités suivantes: itération, accès à l'index, indexOf, lastIndexOf, obtenir en premier, obtenir en dernier, obtenir la dernière, offre, capacité restante, augmenter la capacité, dequeue en dernier, dequeue en premier lieu, mettre en file d'attente/ajouter un élément, retirer de la file d'attente/supprimer un élément, subQueueCopy, subArrayCopy, toArray, snapshot, des bases telles que size, remove ou contient.

EjectingQueue

EjectingIntQueue

1
Terran