/** * 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 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 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); } }