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 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 }