RingBuffer.java

package com.renomad.minum.utils;

import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

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

    /**
     * The size of the inner array supporting this class.  Also,
     * the most items our RingBuffer can contain.
     */
    private final int limit;

    /**
     * The last index of the array - just the limit minus 1.
     */
    private final int lastIndex;

    /**
     * An instance of the class for this generic collection, used to
     * help build data structures.
     */
    private final T[] innerArray;

    /**
     * Index of where we place the next item
     */
    private int nextIndex;

    /**
     * Build a {@link RingBuffer}
     * @param limit the maximum size of the buffer
     */
    public RingBuffer(int limit, Class<T> clazz) {
        this.limit = limit;
        this.lastIndex = limit - 1;
        @SuppressWarnings("unchecked")
        final T[] t = (T[]) Array.newInstance(clazz, limit);
        this.innerArray = t;
        this.nextIndex = 0;
    }

    /**
     * This will move the index forward and
     * wrap around at the end.
     */
    private int incrementIndex(int index) {
        if (index == lastIndex) {
            return 0;
        } else {
            return index + 1;
        }
    }

    public void add(T item) {
        innerArray[nextIndex] = item;
        nextIndex = incrementIndex(nextIndex);
    }

    public int getLimit() {
        return limit;
    }

    /**
     * Returns true if the data in the "myList" parameter is found
     * within the RingBuffer.
     */
    public boolean contains(List<T> myList) {
        if (myList == null || myList.isEmpty()) {
            throw new UtilsException("expected a valid non-empty list to search for in the RingBuffer");
        }
        int myListIndex = 0;
        int myListLength = myList.size();
        for (var value : this) {
            if (myList.get(myListIndex).equals(value)) {
                myListIndex += 1;
            } else {
                myListIndex = 0;
            }

            if (myListIndex == myListLength) {
                return true;
            }

        }
        return false;
    }

    /**
     * Returns true if the data in the "myList" parameter is found
     * within the RingBuffer at the index provided.
     */
    public boolean containsAt(List<T> myList, int index) {
        if (myList == null || myList.isEmpty()) {
            throw new UtilsException("expected a valid non-empty list to search for in the RingBuffer");
        }
        if (index > lastIndex || index < 0) {
            throw new UtilsException("expected an index greater than zero and less-than-or-equal to the last index of the buffer (the limit minus one)");
        }
        int i = 0;
        int myListIndex = 0;
        int myListLastIndex = myList.size() - 1;
        boolean comparing = false;
        var iterator = this.iterator();
        while (true) {
            var value = iterator.next();
            if (i == index) {
                comparing = true;
            }

            if (comparing) {
                if (! myList.get(myListIndex).equals(value)) {
                    return false;
                }
                if (myListIndex == myListLastIndex) {
                    return true;
                }
                myListIndex += 1;
            }

            i += 1;
        }
    }

    @Override
    public Iterator<T> iterator() {
        return new Iterator<>() {

            /**
             * As we loop through the buffer, we have to use a similar
             * technique to the "nextIndex" - wrapping around when we
             * hit the end
             */
            int iteratorIndex = nextIndex;
            /**
             * We'll count as we read values, stopping when we've read every slot.
             */
            int count = 0;

            @Override
            public boolean hasNext() {
                return count < limit;
            }

            @Override
            public T next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
                var result = innerArray[iteratorIndex];
                iteratorIndex = incrementIndex(iteratorIndex);
                count += 1;
                return result;
            }
        };
    }

    /**
     * Returns the value at the slot pointed to by the "nextIndex".  Note that
     * this will be null at first while the array starts to get filled.
     */
    public T atNextIndex() {
        return innerArray[nextIndex];
    }

}