001: /*
002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.util.concurrent;
006:
007: import java.util.AbstractSet;
008: import java.util.Arrays;
009: import java.util.Collection;
010: import java.util.Hashtable;
011: import java.util.Iterator;
012: import java.util.Map;
013: import java.util.Set;
014: import java.util.Map.Entry;
015:
016: /**
017: * This class provides a thread safe map interface (by extending Hashtable) and adds a way to easily and synchronously
018: * iterator over the list of values as an array. This map is very useful when you want a snap shot of the values to
019: * iterator over and dont want to hold up access to the map the whole time while you are iteratoring over the list to
020: * avoid concurrent modification exception.
021: * <p>
022: * For example : <code>
023: * Hashtable t = ....
024: * for(Iterator i = t.values().iterator(); i.hashNext(); ) {
025: * // do something
026: * }
027: * </code>
028: * In the above code, if multiple threads are accessing t, to avoid ConcurrentModificationException, you need to
029: * synchronize the entire for loop.
030: * <p>
031: * Using CopyOnWriteArrayMap and using the values() method will give you a snapshot of the values thus avoid
032: * synchronizing the map for the entire duration of the for loop.
033: * <p>
034: * This is achieved by maintaining an internal copy of the values in an array and copying that on modification. So an in
035: * any CopyOnWrite class this is only effective on small datasets with lots of reads and few writes.
036: */
037: public class CopyOnWriteArrayMap extends Hashtable {
038:
039: public interface TypedArrayFactory {
040: public Object[] createTypedArray(int size);
041: }
042:
043: private static final TypedArrayFactory OBJECT_ARRAY_FACTORY = new TypedArrayFactory() {
044: public Object[] createTypedArray(int size) {
045: return new Object[size];
046: }
047: };
048:
049: private volatile Object _values[];
050: private final TypedArrayFactory _factory;
051:
052: public CopyOnWriteArrayMap() {
053: this (OBJECT_ARRAY_FACTORY);
054: }
055:
056: public CopyOnWriteArrayMap(int initialCapacity, float loadFactor) {
057: this (initialCapacity, loadFactor, OBJECT_ARRAY_FACTORY);
058: }
059:
060: public CopyOnWriteArrayMap(int initialCapacity) {
061: this (initialCapacity, OBJECT_ARRAY_FACTORY);
062: }
063:
064: public CopyOnWriteArrayMap(TypedArrayFactory factory) {
065: _factory = factory;
066: _values = _factory.createTypedArray(0);
067: }
068:
069: public CopyOnWriteArrayMap(int initialCapacity, float loadFactor,
070: TypedArrayFactory factory) {
071: super (initialCapacity, loadFactor);
072: _factory = factory;
073: _values = _factory.createTypedArray(0);
074: }
075:
076: public CopyOnWriteArrayMap(int initialCapacity,
077: TypedArrayFactory factory) {
078: super (initialCapacity);
079: _factory = factory;
080: _values = _factory.createTypedArray(0);
081: }
082:
083: public synchronized void clear() {
084: super .clear();
085: _values = _factory.createTypedArray(0);
086: }
087:
088: /**
089: * This returns a Read only set since remove is not implemented on the set due to lack to time.
090: */
091: public Set entrySet() {
092: return new ReadOnlyEntrySet(super .entrySet());
093: }
094:
095: /**
096: * This returns a Read only set since remove is not implemented on the set due to lack to time.
097: */
098: public Set keySet() {
099: return new ReadOnlySet(super .keySet());
100: }
101:
102: public synchronized Object put(Object key, Object value) {
103: Object old = super .put(key, value);
104: if (old == null) {
105: Object[] old_values = _values;
106: _values = _factory.createTypedArray(old_values.length + 1);
107: System.arraycopy(old_values, 0, _values, 0,
108: old_values.length);
109: _values[old_values.length] = value;
110: } else {
111: Object[] old_values = _values;
112: int length = old_values.length;
113: // XXX:: doing an explicit copy so that the previous snapshots are not messed upon.
114: _values = _factory.createTypedArray(length);
115: for (int i = 0; i < length; i++) {
116: _values[i] = (old == old_values[i] ? value
117: : old_values[i]);
118: }
119: }
120: return old;
121: }
122:
123: public synchronized void putAll(Map t) {
124: // calls into put anyways
125: super .putAll(t);
126: }
127:
128: public synchronized Object remove(Object key) {
129: Object old = super .remove(key);
130: if (old != null) {
131: Object[] old_values = _values;
132: int length = old_values.length;
133: _values = _factory.createTypedArray(length - 1);
134: int i = 0;
135: boolean found = false;
136: for (int j = 0; j < length; j++) {
137: if (found || old != old_values[j]) {
138: _values[i++] = old_values[j];
139: } else {
140: found = true;
141: }
142: }
143: }
144: return old;
145: }
146:
147: public synchronized Collection values() {
148: return Arrays.asList(_values);
149: }
150:
151: public synchronized Object[] valuesToArray() {
152: return _values;
153: }
154:
155: public static class ReadOnlyEntrySet extends ReadOnlySet {
156:
157: public ReadOnlyEntrySet(Set set) {
158: super (set);
159: }
160:
161: public Iterator iterator() {
162: return new ReadOnlyEntrySetIterator(set.iterator());
163: }
164: }
165:
166: public static class ReadOnlySet extends AbstractSet {
167:
168: protected final Set set;
169:
170: public ReadOnlySet(Set set) {
171: this .set = set;
172: }
173:
174: public boolean add(Object o) {
175: throw new UnsupportedOperationException();
176: }
177:
178: public boolean addAll(Collection c) {
179: throw new UnsupportedOperationException();
180: }
181:
182: public void clear() {
183: throw new UnsupportedOperationException();
184: }
185:
186: public boolean contains(Object o) {
187: return set.contains(o);
188: }
189:
190: public boolean containsAll(Collection c) {
191: return set.containsAll(c);
192: }
193:
194: public boolean isEmpty() {
195: return set.isEmpty();
196: }
197:
198: public Iterator iterator() {
199: return new ReadOnlyIterator(set.iterator());
200: }
201:
202: public boolean remove(Object o) {
203: throw new UnsupportedOperationException();
204: }
205:
206: public boolean removeAll(Collection c) {
207: throw new UnsupportedOperationException();
208: }
209:
210: public boolean retainAll(Collection c) {
211: throw new UnsupportedOperationException();
212: }
213:
214: public int size() {
215: return set.size();
216: }
217:
218: }
219:
220: public static class ReadOnlyEntrySetIterator extends
221: ReadOnlyIterator {
222:
223: public ReadOnlyEntrySetIterator(Iterator iterator) {
224: super (iterator);
225: }
226:
227: public Object next() {
228: return new ReadOnlyEntry((Map.Entry) iterator.next());
229: }
230: }
231:
232: public static class ReadOnlyIterator implements Iterator {
233:
234: protected final Iterator iterator;
235:
236: public ReadOnlyIterator(Iterator iterator) {
237: this .iterator = iterator;
238: }
239:
240: public boolean hasNext() {
241: return iterator.hasNext();
242: }
243:
244: public Object next() {
245: return iterator.next();
246: }
247:
248: public void remove() {
249: throw new UnsupportedOperationException();
250: }
251:
252: }
253:
254: public static class ReadOnlyEntry implements Map.Entry {
255:
256: private final Entry entry;
257:
258: public ReadOnlyEntry(Entry entry) {
259: this .entry = entry;
260: }
261:
262: public Object getKey() {
263: return entry.getKey();
264: }
265:
266: public Object getValue() {
267: return entry.getValue();
268: }
269:
270: public Object setValue(Object value) {
271: throw new UnsupportedOperationException();
272: }
273: }
274: }
|