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 net.jcip.annotations.ThreadSafe;
20
21 import java.io.Serializable;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.LinkedHashMap;
25 import java.util.Map;
26 import java.util.WeakHashMap;
27
28 /**
29 * A thread-safe variant of {@link Map} in which all mutative operations (the "destructive"
30 * operations described by {@link Map} put, remove and so on) are implemented by making a fresh copy
31 * of the underlying map.
32 * <p>
33 * This is ordinarily too costly, but may be <em>more</em> efficient than alternatives when
34 * traversal operations vastly out-number mutations, and is useful when you cannot or don't want to
35 * synchronize traversals, yet need to preclude interference among concurrent threads. The
36 * "snapshot" style iterators on the collections returned by {@link #entrySet()}, {@link #keySet()}
37 * and {@link #values()} use a reference to the internal map at the point that the iterator was
38 * created. This map never changes during the lifetime of the iterator, so interference is
39 * impossible and the iterator is guaranteed not to throw <tt>ConcurrentModificationException</tt>.
40 * The iterators will not reflect additions, removals, or changes to the list since the iterator was
41 * created. Removing elements via these iterators is not supported. The mutable operations on these
42 * collections (remove, retain etc.) are supported but as with the {@link Map} interface, add and
43 * addAll are not and throw {@link UnsupportedOperationException}.
44 * <p>
45 * The actual copy is performed by a supplied {@link CopyFunction} object. The Factory is
46 * responsible for the underlying Map implementation (for instance a HashMap, TreeMap,
47 * ListOrderedMap etc.) and therefore the semantics of what this map will cope with as far as null
48 * keys and values, iteration ordering etc.
49 * <p>
50 * There are supplied {@link Functions} for the common Collections {@link Map} implementations.
51 * <p>
52 * Views of the keys, values and entries are modifiable and will cause a copy.
53 * <p>
54 * <strong>Please note</strong> that the thread-safety guarantees are limited to the thread-safety
55 * of the non-mutative (non-destructive) operations of the underlying map implementation. For
56 * instance some implementations such as {@link WeakHashMap} and {@link LinkedHashMap} with access
57 * ordering are actually structurally modified by the {@link #get(Object)} method and are therefore
58 * not suitable candidates as delegates for this class.
59 */
60 @ThreadSafe public abstract class CopyOnWriteMap<K, V> extends AbstractCopyOnWriteMap<K, V, Map<K, V>> implements Map<K, V>, Serializable {
61 private static final long serialVersionUID = 7935514534647505917L;
62
63 public interface CopyFunction<M extends Map<?, ?>> extends AbstractCopyOnWriteMap.CopyFunction<M> {}
64
65 //
66 // factory methods
67 //
68
69 /**
70 * Creates a new {@link CopyOnWriteMap} with an underlying {@link HashMap}.
71 */
72 public static <K, V> CopyOnWriteMap<K, V> newHashMap() {
73 return new CopyOnWriteMap<K, V>() {
74 @Override public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) {
75 return new HashMap<K, V>(map);
76 }
77 };
78 }
79
80 /**
81 * Creates a new {@link CopyOnWriteMap} with an underlying {@link HashMap} using the supplied
82 * map as the initial values.
83 */
84 public static <K, V> CopyOnWriteMap<K, V> newHashMap(final Map<? extends K, ? extends V> map) {
85 return new CopyOnWriteMap<K, V>(map) {
86 @Override 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 LinkedHashMap}. Iterators for
94 * this map will be return elements in insertion order.
95 */
96 public static <K, V> CopyOnWriteMap<K, V> newLinkedMap() {
97 return new CopyOnWriteMap<K, V>() {
98 @Override public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) {
99 return new LinkedHashMap<K, V>(map);
100 }
101 };
102 }
103
104 /**
105 * Creates a new {@link CopyOnWriteMap} with an underlying {@link LinkedHashMap} using the
106 * supplied map as the initial values. Iterators for this map will be return elements in
107 * insertion order.
108 */
109 public static <K, V> CopyOnWriteMap<K, V> newLinkedMap(final Map<? extends K, ? extends V> map) {
110 return new CopyOnWriteMap<K, V>(map) {
111 @Override public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) {
112 return new LinkedHashMap<K, V>(map);
113 }
114 };
115 }
116
117 //
118 // constructors
119 //
120
121 /**
122 * Create a new {@link CopyOnWriteMap} with the supplied {@link Map} to initialize the values
123 * and the {@link CopyFunction} for creating our actual delegate instances.
124 *
125 * @param map the initial map to initialize with
126 * @param factory the copy function
127 */
128 public CopyOnWriteMap(final Map<? extends K, ? extends V> map) {
129 super(map);
130 }
131
132 /**
133 * Create a new empty {@link CopyOnWriteMap} with the {@link CopyFunction} for creating our
134 * actual delegate instances.
135 *
136 * @param factory the copy function
137 */
138 public CopyOnWriteMap() {
139 super(Collections.<K, V> emptyMap());
140 }
141
142 @Override public abstract <N extends Map<? extends K, ? extends V>> Map<K, V> copy(N map);
143 }