/**
* Copyright 2012 by dueni.ch
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dueni.util.collections;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* CreateOnWriteList
is intended to be used for most likely empty lists within objects
* that are intended to keep in session and therefore may occupy memory for long time.
* CreateOnWriteList
in best usage case will use 0 bytes of memory and still provides a
* fully operable List implementation using a call-back method to create the real list before first
* {@link List#add(Object)} operation is executed.
*
* Memory analysis for different types of lists show:
*
*
*
* Nr Test case retained shallow
* == ================================================== ========= =======
* 1 ArrayList (default size) 80 24
* 2 ArrayList (size 0) 40 24
* 3 LinkedList (empty) 48 24
* 4 CreatOnWriteList (empty, assigned to var) 32 16
* 5 CreatOnWriteList (return new from getList() method) 0 0
*
*
* Example code for above test nr 4
*
*
* public class MyOwner {
* private List<String> list;
*
* public MyOwner() {
* list = new CreateOnWriteList<String>() {
* @Override
* public List<String> newList() {
* list = new ArrayList<String>(1); // init with minimal size to use less memory
* return list;
* }
* };
* }
*
* public List<String> getList() {
* return list;
* }
* }
*
*
* Example code for above test nr 5
*
*
* public class MyOwner {
* private List<String> list;
*
* public MyOwner() {
* }
*
* public List<String> getList() {
* if (list == null) {
* return new CreateOnWriteList<String>() {
* @Override
* public List<String> newList() {
* list = new ArrayList<String>(1); // init with minimal size to use less memory
* return list;
* }
* };
* }
* return list;
* }
* }
*
*
* Which variant to use?
*
* It is recommended to return new CreateOnWriteList within the get-method as shown in
* "example code for test nr 5" unless you have very frequent access to empty lists without adding
* entries.
*
*
* @author Hanspeter Dünnenberger
*/
public abstract class CreateOnWriteList implements List {
/** local variable for the wrapped list to create as late as possible */
private List wrapped;
/**
* Return the just created real List after assigning it to the owning object's member variable.
*
*
*
* public class OwningType {
* private List list;
*
* public List getList() {
* if (list == null) {
* return new CreateOnWriteList() {
*
* @Override
* public List newList() {
* list = new ArrayList(1);
* return list;
* }
* };
* }
* return list;
* }
* }
*
*
*
* @return the just created real List after assigning it to the owning object's member variable.
*/
public abstract List newList();
/**
* Make sure wrapped is assigned from {@link #newList()} return the wrapped List.
*
* @return the real List as returned and kept from the {@link #newList()}.
*/
private List getWrapped() {
if (wrapped == null) {
wrapped = newList();
}
return wrapped;
}
/**
* Make sure wrapped is assigned from {@link #newList()} and delegate the passed argument to the
* wrapped List.
*
* @see List#add(Object)
*/
@Override
public boolean add(E e) {
return getWrapped().add(e);
}
/**
* Make sure wrapped is assigned from {@link #newList()} and delegate the passed argument to the
* wrapped List.
*
* @see List#add(int, Object)
*/
@Override
public void add(int index, E element) {
getWrapped().add(index, element);
}
/**
* Make sure wrapped is assigned from {@link #newList()} and delegate the passed argument to the
* wrapped List.
*
* @see List#addAll(Collection)
*/
@Override
public boolean addAll(Collection extends E> c) {
return getWrapped().addAll(c);
}
/**
* Make sure wrapped is assigned from {@link #newList()} and delegate the passed argument to the
* wrapped List.
*
* @see List#addAll(int, Collection)
*/
@Override
public boolean addAll(int index, Collection extends E> c) {
return getWrapped().addAll(index, c);
}
@Override
public void clear() {
if (wrapped != null) {
wrapped.clear();
}
}
@Override
public boolean contains(Object o) {
if (wrapped != null) {
return wrapped.contains(o);
}
return false;
}
@Override
public boolean containsAll(Collection> c) {
if (wrapped != null) {
return wrapped.containsAll(c);
}
return false;
}
@Override
public E get(int index) {
if (wrapped != null) {
return wrapped.get(index);
}
return null;
}
@Override
public int indexOf(Object o) {
if (wrapped != null) {
return wrapped.indexOf(o);
}
return -1;
}
@Override
public boolean isEmpty() {
if (wrapped != null) {
return wrapped.isEmpty();
}
return true;
}
@Override
@SuppressWarnings("unchecked")
public Iterator iterator() {
if (wrapped != null) {
return wrapped.iterator();
}
return Collections.EMPTY_LIST.iterator();
}
@Override
public int lastIndexOf(Object o) {
if (wrapped != null) {
return wrapped.lastIndexOf(o);
}
return -1;
}
@Override
@SuppressWarnings("unchecked")
public ListIterator listIterator() {
if (wrapped != null) {
return wrapped.listIterator();
}
return Collections.EMPTY_LIST.listIterator();
}
@Override
@SuppressWarnings("unchecked")
public ListIterator listIterator(int index) {
if (wrapped != null) {
return wrapped.listIterator(index);
}
return Collections.EMPTY_LIST.listIterator();
}
@Override
public boolean remove(Object o) {
if (wrapped != null) {
return wrapped.remove(o);
}
return false;
}
@Override
public E remove(int index) {
if (wrapped != null) {
return wrapped.remove(index);
}
return null;
}
@Override
public boolean removeAll(Collection> c) {
if (wrapped != null) {
return wrapped.removeAll(c);
}
return false;
}
@Override
public boolean retainAll(Collection> c) {
if (wrapped != null) {
return wrapped.retainAll(c);
}
return false;
}
/**
* If {@link #newList()} was used before, delegate to the wrapped list.
*
* @throws UnsupportedOperationException if {@link #newList()} was not called before.
*
* @see List#set(int, Object)
*/
@Override
public E set(int index, E element) {
if (wrapped != null) {
return wrapped.set(index, element);
}
throw new UnsupportedOperationException();
}
@Override
public int size() {
if (wrapped != null) {
return wrapped.size();
}
return 0;
}
@Override
@SuppressWarnings("unchecked")
public List subList(int fromIndex, int toIndex) {
if (wrapped != null) {
return wrapped.subList(fromIndex, toIndex);
}
return Collections.EMPTY_LIST;
}
@Override
public Object[] toArray() {
if (wrapped != null) {
return wrapped.toArray();
}
return Collections.EMPTY_LIST.toArray();
}
@Override
public T[] toArray(T[] a) {
if (wrapped != null) {
return wrapped.toArray(a);
}
return a;
}
@Override
public String toString() {
if (wrapped != null) {
return wrapped.toString();
}
return Collections.EMPTY_LIST.toString();
}
@Override
public boolean equals(Object obj) {
if (wrapped != null) {
return wrapped.equals(obj);
}
return super.equals(obj);
}
}