14

It's useful to me to have a data structure in Java that has all the functionality of a List, but has a maximum storage capacity, and drops older data when newer data is added. Conceivably at some point I might want to implement a fixed size Queue which keeps a more general ordering of the data, and drops the old data lowest in that ordering, but that's the for the future.

At the moment I'm implementing it like this:

public class FixedSizeList<T> {

  private final int maxSize;
  private final LinkedList<T> list = new LinkedList<T>();

  public FixedSizeQueue(int maxSize) {
    this.maxSize = maxSize < 0 ? 0 : maxSize;
  }

  public T add(T t) {
    list.add(t);
    return list.size() > maxSize ? list.remove() : null;
  }

  // add remaining methods...

}

Is there either (a) an existing data structure that serves my needs, or (b) a better way of implementing this data structure?

Gavriel
  • 18,880
  • 12
  • 68
  • 105
Chris Taylor
  • 46,912
  • 15
  • 110
  • 154
  • @Fabrizio I searched through the past questions, but couldn't find one with the same specifications as mine. I guess as much as a practical solution, I'm looking for the 'canonical' or 'best possible' solution. – Chris Taylor May 17 '11 at 08:29
  • Sounds like you want a circular buffer. This would be more efficient than deleting from the start or an ArrayList or using LinkedList – Peter Lawrey May 17 '11 at 09:02
  • I think your approach is quit good, since you are using aggregation, but consider implementing List interface. – Rrr Aug 10 '12 at 21:06
  • I think this is not a duplicate, as he clearly asks for "List, but has a maximum storage capacity, and drops older data when newer data is added", while in the other question it is not clear what he wanted, and all the answers create a fixed-size list instead, which are not an acceptable answer to this question. – Gavriel Jan 27 '16 at 12:00
  • I think you meant to leave this comment on [this question](http://stackoverflow.com/questions/15641411/how-to-create-a-linked-list-with-a-given-sizejava?lq=1) – Chris Taylor Jan 27 '16 at 13:39

4 Answers4

6

I would use array and 2 indexes for head and tail of the list.Make sure that head is always < tail and you're safe.

jdevelop
  • 12,176
  • 10
  • 56
  • 112
5

Unless you want to use an actual array, I don't believe there is a list type data structure you can use.

Personally I would extend one of the existing list classes to get the functionality, and override the add methods. This way you get all the other list operations for free. ie something like the following...

public class FixedSizeArrayList<T> extends ArrayList<T> {
    private final int maxSize;
    public FixedSizeArrayList(int maxSize) {
        super();
        this.maxSize = maxSize
    }

    public boolean add(T t) {
        if (size() >= maxSize) {
            remove(0);
        }
        return super.add(t);
    }

    // implementation of remaining add methods....
}
Tom Jefferys
  • 13,090
  • 2
  • 35
  • 36
  • the complexity of this operation would be O(N), so I doubt is it acceptable at all. – jdevelop May 17 '11 at 08:58
  • same reason for downvote - please keep the **composition** pattern and do not suggest **inheritance**. Implement the `List` interface and add a private field `List innerList = new ArrayList();` (like Chris in his question) – Andreas Dolk May 17 '11 at 09:08
  • 2
    Why in this specific example? Yes I agree in general composition is better than inheritance. However I think this is a specific case where you'd want to take advantage of the benefits that inheritance brings. – Tom Jefferys May 17 '11 at 09:11
  • Chris already used composition - a suggestion to replace it with inheritance should come with an explanation. – Andreas Dolk May 17 '11 at 09:54
5

Here's a List with a size limit, based on Guava's ForwardingList:

A list which forwards all its method calls to another list. Subclasses should override one or more methods to modify the behavior of the backing list as desired per the decorator pattern.

Guava has base classes like this for all JDK-5 Collection types. Each of them fulfills the same purpose: making it easy to add value, while delegating all default functionality to the underlying collection.

public class LimitingList<E> extends ForwardingList<E> {

    private final class LimitingListIterator extends ForwardingListIterator<E> {

        private final ListIterator<E> innerListIterator;

        private LimitingListIterator(final ListIterator<E> innerListIterator) {
            this.innerListIterator = innerListIterator;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void add(final E element) {
            if (inner.size() < maxSize)
                innerListIterator.add(element);
            else
                throw new IndexOutOfBoundsException();
        }

        @Override
        protected ListIterator<E> delegate() {
            return innerListIterator;
        }
    }

    public LimitingList(final int maxSize) {
        this(new ArrayList<E>(), maxSize);
    }

    public LimitingList(final List<E> inner, final int maxSize) {
        super();
        this.inner = inner;
        this.maxSize = maxSize;
    }

    @Override
    public boolean addAll(final Collection<? extends E> collection) {
        boolean changed = false;
        for (final E item : collection) {
            final boolean tmpChanged = add(item);
            changed = changed || tmpChanged;
            if (!tmpChanged)
                break;
        }
        return changed;
    }

    @Override
    public boolean add(final E e) {
        if (inner.size() < maxSize)
            return super.add(e);
        else
            return false;
    }

    @Override
    public ListIterator<E> listIterator() {
        return new LimitingListIterator(inner.listIterator());
    }

    @Override
    public void add(final int index, final E element) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean addAll(final int index, final Collection<? extends E> elements) {
        throw new UnsupportedOperationException();
    }

    @Override
    public ListIterator<E> listIterator(final int index) {
        return new LimitingListIterator(inner.listIterator(index));
    }

    private final int maxSize;
    private final List<E> inner;

    @Override
    protected List<E> delegate() {
        return inner;
    }

}

It delegates all real functionality to an underlying list, which is an ArrayList per default (single argument constructor), but you can also supply (two argument constructor)

naimdjon
  • 3,162
  • 1
  • 20
  • 41
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • @Andreas ??? I *am* using Composition, but I am using an adapter class to simplify things. Why should I re-invent the wheel, the List interface is enormous, and `ForwardingList` gives me sensible defaults for most methods for free, while delegating all the logic to the inner List. – Sean Patrick Floyd May 17 '11 at 09:12
  • 1
    **sorry** - didn't scroll, the declaration is at the end of your code ;) – Andreas Dolk May 17 '11 at 09:17
  • @Andreas ok, I guess than I can call back my mafia killer commando :-) – Sean Patrick Floyd May 17 '11 at 09:41
  • 3
    (hmm - maybe I should downvote for declaring the fields near the end of the class body, just to meet your bad guys *fg*) – Andreas Dolk May 17 '11 at 09:43
  • what it the difference with original proposed solution ? Using fancy guava implementation instead of LinkedList ? – Rrr Aug 10 '12 at 21:13
  • The add method doesn't throw away the oldest it simply doesn't add anything once max size is reached! Would you like to fix it? – Dave Moten Jul 03 '14 at 02:20
-2

If you extend the LinkedList class you will have direct access to all it's methods. Instead of having to write stuff like

fixedList.getList().pop() 

you could just write

fixedList.pop()

You could then override the methods where you need to add the maxSize criteria.

vichle
  • 2,499
  • 1
  • 15
  • 17
  • 2
    sry for downvoting, but to my opinion **composition** (like he did) is much better then **inheritance** (like you suggested). – Andreas Dolk May 17 '11 at 08:27
  • Please elaborate on that. Why is his way better? – vichle May 17 '11 at 08:28
  • 1
    there are a lot of Q/A around this topic, here's a good starting point: http://stackoverflow.com/questions/3979581/disadvantage-of-object-composition-over-class-inheritance – Andreas Dolk May 17 '11 at 09:03
  • @Andreas_D Thank you for clearing that up! Sounds very reasonable :) – vichle May 17 '11 at 09:50