View Javadoc

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 }