001package io.ebean.common; 002 003import java.io.Serializable; 004import java.util.AbstractList; 005import java.util.ArrayList; 006import java.util.Collection; 007import java.util.Comparator; 008import java.util.List; 009import java.util.concurrent.locks.ReentrantLock; 010import java.util.function.Predicate; 011import java.util.function.UnaryOperator; 012 013/** 014 * List that copies itself on first write access. Needed to keep memory footprint low and the ability 015 * to modify lists from cache. 016 * 017 * @author Roland Praml, FOCONIS AG 018 */ 019public final class CopyOnFirstWriteList<E> extends AbstractList<E> implements List<E>, Serializable { 020 021 private static final long serialVersionUID = 1L; 022 023 private final ReentrantLock lock = new ReentrantLock(); 024 025 /** 026 * The underlying List implementation. 027 */ 028 private List<E> list; 029 030 public CopyOnFirstWriteList(List<E> list) { 031 super(); 032 this.list = list; 033 } 034 035 private volatile boolean copied = false; 036 037 @Override 038 public int size() { 039 return list.size(); 040 } 041 042 @Override 043 public boolean isEmpty() { 044 return list.isEmpty(); 045 } 046 047 @Override 048 public boolean contains(Object o) { 049 return list.contains(o); 050 } 051 052 @Override 053 public Object[] toArray() { 054 return list.toArray(); 055 } 056 057 @Override 058 public <T> T[] toArray(T[] a) { 059 return list.toArray(a); 060 } 061 062 @Override 063 public boolean add(E e) { 064 checkCopyOnWrite(); 065 return list.add(e); 066 } 067 068 @Override 069 public boolean remove(Object o) { 070 checkCopyOnWrite(); 071 return list.remove(o); 072 } 073 074 @Override 075 public boolean containsAll(Collection<?> c) { 076 return list.containsAll(c); 077 } 078 079 @Override 080 public boolean addAll(Collection<? extends E> c) { 081 checkCopyOnWrite(); 082 return list.addAll(c); 083 } 084 085 @Override 086 public boolean addAll(int index, Collection<? extends E> c) { 087 checkCopyOnWrite(); 088 return list.addAll(index, c); 089 } 090 091 @Override 092 public boolean removeAll(Collection<?> c) { 093 checkCopyOnWrite(); 094 return list.removeAll(c); 095 } 096 097 @Override 098 public boolean retainAll(Collection<?> c) { 099 checkCopyOnWrite(); 100 return list.retainAll(c); 101 } 102 103 @Override 104 public void replaceAll(UnaryOperator<E> operator) { 105 checkCopyOnWrite(); 106 list.replaceAll(operator); 107 } 108 109 @Override 110 public boolean removeIf(Predicate<? super E> filter) { 111 checkCopyOnWrite(); 112 return list.removeIf(filter); 113 } 114 115 @Override 116 public void sort(Comparator<? super E> c) { 117 checkCopyOnWrite(); 118 list.sort(c); 119 } 120 121 @Override 122 public void clear() { 123 if (!copied) { 124 list = new ArrayList<>(); 125 copied = true; 126 } 127 } 128 129 @Override 130 public boolean equals(Object o) { 131 return list.equals(o); 132 } 133 134 @Override 135 public int hashCode() { 136 return list.hashCode(); 137 } 138 139 @Override 140 public E get(int index) { 141 return list.get(index); 142 } 143 144 @Override 145 public E set(int index, E element) { 146 checkCopyOnWrite(); 147 return list.set(index, element); 148 } 149 150 @Override 151 public void add(int index, E element) { 152 checkCopyOnWrite(); 153 list.add(index, element); 154 } 155 156 @Override 157 public E remove(int index) { 158 checkCopyOnWrite(); 159 return list.remove(index); 160 } 161 162 @Override 163 public int indexOf(Object o) { 164 return list.indexOf(o); 165 } 166 167 @Override 168 public int lastIndexOf(Object o) { 169 return list.lastIndexOf(o); 170 } 171 172 private void checkCopyOnWrite() { 173 if (!copied) { 174 lock.lock(); 175 try { 176 if (!copied) { 177 list = new ArrayList<>(list); 178 copied = true; 179 } 180 } finally { 181 lock.unlock(); 182 } 183 } 184 } 185}