1 /**
2 * Copyright 2008 Atlassian Pty Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.atlassian.util.concurrent;
18
19 import java.io.Serializable;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.LinkedHashMap;
23 import java.util.Map;
24 import java.util.TreeMap;
25 import java.util.WeakHashMap;
26
27 import net.jcip.annotations.GuardedBy;
28 import net.jcip.annotations.ThreadSafe;
29
30 /**
31 * A thread-safe variant of {@link Map} in which all mutative operations (the
32 * "destructive" operations described by {@link Map} put, remove and so on) are
33 * implemented by making a fresh copy of the underlying map.
34 * <p>
35 * This is ordinarily too costly, but may be <em>more</em> efficient than
36 * alternatives when traversal operations vastly out-number mutations, and is
37 * useful when you cannot or don't want to synchronize traversals, yet need to
38 * preclude interference among concurrent threads. The "snapshot" style
39 * iterators on the collections returned by {@link #entrySet()},
40 * {@link #keySet()} and {@link #values()} use a reference to the internal map
41 * at the point that the iterator was created. This map never changes during the
42 * lifetime of the iterator, so interference is impossible and the iterator is
43 * guaranteed not to throw <tt>ConcurrentModificationException</tt>. The
44 * iterators will not reflect additions, removals, or changes to the list since
45 * the iterator was created. Removing elements via these iterators is not
46 * supported. The mutable operations on these collections (remove, retain etc.)
47 * are supported but as with the {@link Map} interface, add and addAll are not
48 * and throw {@link UnsupportedOperationException}.
49 * <p>
50 * The actual copy is performed by an abstract {@link #copy(Map)} method. The
51 * method is responsible for the underlying Map implementation (for instance a
52 * {@link HashMap}, {@link TreeMap}, {@link LinkedHashMap} etc.) and therefore
53 * the semantics of what this map will cope with as far as null keys and values,
54 * iteration ordering etc. See the note below about suitable candidates for
55 * underlying Map implementations
56 * <p>
57 * There are supplied implementations for the common Collections {@link Map}
58 * implementations via the {@link CopyOnWriteMaps} static factory methods.
59 * <p>
60 * Collection views of the keys, values and entries are modifiable and will
61 * cause a copy.
62 * <p>
63 * <strong>Please note</strong> that the thread-safety guarantees are limited to
64 * the thread-safety of the non-mutative (non-destructive) operations of the
65 * underlying map implementation. For instance some implementations such as
66 * {@link WeakHashMap} and {@link LinkedHashMap} with access ordering are
67 * actually structurally modified by the {@link #get(Object)} method and are
68 * therefore not suitable candidates as delegates for this class.
69 *
70 * @param <K> the key type
71 * @param <V> the value type
72 * @author Jed Wesley-Smith
73 */
74 @ThreadSafe
75 public abstract class CopyOnWriteMap<K, V> extends AbstractCopyOnWriteMap<K, V, Map<K, V>> implements Map<K, V>, Serializable {
76 private static final long serialVersionUID = 7935514534647505917L;
77
78 /**
79 * Creates a new {@link CopyOnWriteMap} with an underlying {@link HashMap}.
80 */
81 public static <K, V> CopyOnWriteMap<K, V> newHashMap() {
82 return new CopyOnWriteMap<K, V>() {
83 private static final long serialVersionUID = 5221824943734164497L;
84
85 @Override
86 public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) {
87 return new HashMap<K, V>(map);
88 }
89 };
90 }
91
92 /**
93 * Creates a new {@link CopyOnWriteMap} with an underlying {@link HashMap}
94 * using the supplied map as the initial values.
95 */
96 public static <K, V> CopyOnWriteMap<K, V> newHashMap(final Map<? extends K, ? extends V> map) {
97 return new CopyOnWriteMap<K, V>(map) {
98 private static final long serialVersionUID = -7616159260882572421L;
99
100 @Override
101 public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) {
102 return new HashMap<K, V>(map);
103 }
104 };
105 }
106
107 /**
108 * Creates a new {@link CopyOnWriteMap} with an underlying
109 * {@link LinkedHashMap}. Iterators for this map will be return elements in
110 * insertion order.
111 */
112 public static <K, V> CopyOnWriteMap<K, V> newLinkedMap() {
113 return new CopyOnWriteMap<K, V>() {
114 private static final long serialVersionUID = -4597421704607601676L;
115
116 @Override
117 public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) {
118 return new LinkedHashMap<K, V>(map);
119 }
120 };
121 }
122
123 /**
124 * Creates a new {@link CopyOnWriteMap} with an underlying
125 * {@link LinkedHashMap} using the supplied map as the initial values.
126 * Iterators for this map will be return elements in insertion order.
127 */
128 public static <K, V> CopyOnWriteMap<K, V> newLinkedMap(final Map<? extends K, ? extends V> map) {
129 return new CopyOnWriteMap<K, V>(map) {
130 private static final long serialVersionUID = -8659999465009072124L;
131
132 @Override
133 public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) {
134 return new LinkedHashMap<K, V>(map);
135 }
136 };
137 }
138
139 //
140 // constructors
141 //
142
143 /**
144 * Create a new {@link CopyOnWriteMap} with the supplied {@link Map} to
145 * initialize the values.
146 *
147 * @param map the initial map to initialize with
148 * @param factory the copy function
149 */
150 public CopyOnWriteMap(final Map<? extends K, ? extends V> map) {
151 super(map);
152 }
153
154 /**
155 * Create a new empty {@link CopyOnWriteMap}.
156 *
157 * @param factory the copy function
158 */
159 public CopyOnWriteMap() {
160 super(Collections.<K, V> emptyMap());
161 }
162
163 @Override
164 @GuardedBy("internal-lock")
165 protected abstract <N extends Map<? extends K, ? extends V>> Map<K, V> copy(N map);
166 }