Tutorial :java: List wrapper where get()/set() is allowed but add/remove is not



Question:

I need to wrap a List<T> with some class that allows calls to set/get but does not allow add/remove calls, so that the list remains "stuck" at a fixed length. I think I have a thin wrapper class (below) that will work, but I'm not 100% positive.

Did I miss anything obvious?

import java.util.Collection;  import java.util.Iterator;  import java.util.List;  import java.util.ListIterator;    class RestrictedListWrapper<T> implements List<T>  {      static <T> T fail() throws UnsupportedOperationException      {          throw new UnsupportedOperationException();      }           static private class IteratorWrapper<T> implements ListIterator<T>      {          final private ListIterator<T> iter;            private IteratorWrapper(ListIterator<T> iter) { this.iter = iter; }          static public <T> RestrictedListWrapper.IteratorWrapper<T> wrap(ListIterator<T> target) {               return new RestrictedListWrapper.IteratorWrapper<T>(target);           }          @Override public void add(T e) { fail(); }          @Override public boolean hasNext() { return this.iter.hasNext(); }          @Override public boolean hasPrevious() { return this.iter.hasPrevious(); }          @Override public T next() { return this.iter.next(); }          @Override public int nextIndex() { return this.iter.nextIndex(); }          @Override public T previous() { return this.iter.previous(); }          @Override public int previousIndex() { return this.iter.previousIndex(); }          @Override public void remove() { fail(); }          @Override public void set(T e) { this.iter.set(e); }      }               final private List<T> list;        private RestrictedListWrapper(List<T> list) {          this.list = list;      }      static public <T> RestrictedListWrapper<T> wrap(List<T> target) {          return new RestrictedListWrapper<T>(target);      }      @Override public boolean add(T arg0) { return fail();  }       @Override public void add(int index, T element) { fail(); }      @Override public boolean addAll(Collection<? extends T> arg0) {          return fail();       }      @Override public boolean addAll(int arg0, Collection<? extends T> arg1) {          return fail();      }        /**       * clear() allows setting all members of the list to null       */      @Override public void clear() {          ListIterator<T> it = this.list.listIterator();            while (it.hasNext())          {              it.set(null);              it.next();          }      }      @Override public boolean contains(Object o) {          return this.list.contains(o);      }      @Override public boolean containsAll(Collection<?> c) {          return this.list.containsAll(c);      }      @Override public T get(int index) { return this.list.get(index); }      @Override public int indexOf(Object o) { return this.list.indexOf(o); }      @Override public boolean isEmpty() { return false; }      @Override public Iterator<T> iterator() {           return listIterator();      }      @Override public int lastIndexOf(Object o) { return this.list.lastIndexOf(o); }      @Override public ListIterator<T> listIterator() {          return IteratorWrapper.wrap(this.list.listIterator());      }      @Override public ListIterator<T> listIterator(int index) {          return IteratorWrapper.wrap(this.list.listIterator(index));      }      @Override public boolean remove(Object o) { return fail(); }      @Override public T remove(int index) { fail(); return fail(); }      @Override public boolean removeAll(Collection<?> c) { return fail(); }      @Override public boolean retainAll(Collection<?> c) { return fail(); }        @Override public T set(int index, T element) { return this.list.set(index, element); }      @Override public int size() { return this.list.size(); }      @Override public List<T> subList(int fromIndex, int toIndex) {          return new RestrictedListWrapper<T>(this.list.subList(fromIndex, toIndex));      }      @Override public Object[] toArray() { return this.list.toArray(); }      @Override public <T> T[] toArray(T[] a) { return this.list.toArray(a); }  }  


Solution:1

Apache Commons Collections has a FixedSizedList class that does exactly that.

Decorates another List to fix the size preventing add/remove.

The add, remove, clear and retain operations are unsupported. The set method is allowed (as it doesn't change the list size).

LarvaLabs supplies a java5-generics-friendly version.


Solution:2

Personally, I wouldn't bother to reinvent the wheel. Apache Commons has a ListUtils. fixedSizeList which does that, and no doubt the Google java classes have one too.


Solution:3

You have missed:

toString  equals  hashCode  

clear breaks LSP.

Should probably implement java.io.Serializable.

The implementation returned should implement java.util.RandomAccess if and only if the target does also. Top marks for using a static creation method instead of a naked constructor.

The target argument should be tested for null at creation time, rather than waiting to call a method on it.

No need for this everywhere.


Solution:4

Do you actually need to wrap an arbitrary backing list? Or do you just need a fixed-size list? If the latter, the JDK has it:

List<Foo> foos = Arrays.asList(new Foo[42]);


Solution:5

Similar to skauffman's answer, you could use the Google Collections library ImmutableList<E>, with a mutable E type. Not quite list.set(i,E), but list.get(i).set(state) could be close enough.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »