/*
 * Copyright 2008-present MongoDB, Inc.
 * Copyright (c) 2008-2014 Atlassian Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bson.util;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

A thread-safe variant of Map in which all mutative operations (the "destructive" operations described by Map put, remove and so on) are implemented by making a fresh copy of the underlying map.

This is ordinarily too costly, but may be more efficient than alternatives when traversal operations vastly out-number mutations, and is useful when you cannot or don't want to synchronize traversals, yet need to preclude interference among concurrent threads. The "snapshot" style iterators on the collections returned by AbstractCopyOnWriteMap.entrySet(), AbstractCopyOnWriteMap.keySet() and AbstractCopyOnWriteMap.values() use a reference to the internal map at the point that the iterator was created. This map never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException. The iterators will not reflect additions, removals, or changes to the list since the iterator was created. Removing elements via these iterators is not supported. The mutable operations on these collections (remove, retain etc.) are supported but as with the Map interface, add and addAll are not and throw UnsupportedOperationException.

The actual copy is performed by an abstract copy(Map) method. The method is responsible for the underlying Map implementation (for instance a HashMap, TreeMap, LinkedHashMap etc.) and therefore the semantics of what this map will cope with as far as null keys and values, iteration ordering etc. See the note below about suitable candidates for underlying Map implementations

There are supplied implementations for the common j.u.c Map implementations via the CopyOnWriteMap static Builder.

Collection views of the keys, values and entries are optionally live or stable. Live views are modifiable will cause a copy if a modifying method is called on them. Methods on these will reflect the current state of the collection, although iterators will be snapshot style. If the collection views are stable they are unmodifiable, and will be a snapshot of the state of the map at the time the collection was asked for.

Please note that the thread-safety guarantees are limited to the thread-safety of the non-mutative (non-destructive) operations of the underlying map implementation. For instance some implementations such as WeakHashMap and LinkedHashMap with access ordering are actually structurally modified by the AbstractCopyOnWriteMap.get(Object) method and are therefore not suitable candidates as delegates for this class.

Author:Jed Wesley-Smith
Type parameters:
  • <K> – the key type
  • <V> – the value type
/** * <p>A thread-safe variant of {@link java.util.Map} in which all mutative operations (the "destructive" operations described by {@link * java.util.Map} put, remove and so on) are implemented by making a fresh copy of the underlying map.</p> * * <p>This is ordinarily too costly, but may be <em>more</em> efficient than alternatives when traversal operations vastly out-number * mutations, and is useful when you cannot or don't want to synchronize traversals, yet need to preclude interference among concurrent * threads. The "snapshot" style iterators on the collections returned by {@link #entrySet()}, {@link #keySet()} and {@link #values()} use a * reference to the internal map at the point that the iterator was created. This map never changes during the lifetime of the iterator, so * interference is impossible and the iterator is guaranteed not to throw {@code ConcurrentModificationException}. The iterators will not * reflect additions, removals, or changes to the list since the iterator was created. Removing elements via these iterators is not * supported. The mutable operations on these collections (remove, retain etc.) are supported but as with the {@link java.util.Map} * interface, add and addAll are not and throw {@link UnsupportedOperationException}.</p> <p>The actual copy is performed by an abstract * {@link #copy(java.util.Map)} method. The method is responsible for the underlying Map implementation (for instance a {@link * java.util.HashMap}, {@link java.util.TreeMap}, {@link java.util.LinkedHashMap} etc.) and therefore the semantics of what this map will * cope with as far as null keys and values, iteration ordering etc. See the note below about suitable candidates for underlying Map * implementations </p> * * <p>There are supplied implementations for the common j.u.c {@link java.util.Map} implementations via the {@link CopyOnWriteMap} static * {@link Builder}. </p> * * <p>Collection views of the keys, values and entries are optionally {@link View.Type#LIVE live} or {@link View.Type#STABLE stable}. Live * views are modifiable will cause a copy if a modifying method is called on them. Methods on these will reflect the current state of the * collection, although iterators will be snapshot style. If the collection views are stable they are unmodifiable, and will be a snapshot * of the state of the map at the time the collection was asked for. </p> * * <p><strong>Please note</strong> that the thread-safety guarantees are limited to the thread-safety of the non-mutative (non-destructive) * operations of the underlying map implementation. For instance some implementations such as {@link java.util.WeakHashMap} and {@link * java.util.LinkedHashMap} with access ordering are actually structurally modified by the {@link #get(Object)} method and are therefore not * suitable candidates as delegates for this class. * * @param <K> the key type * @param <V> the value type * @author Jed Wesley-Smith */
abstract class CopyOnWriteMap<K, V> extends AbstractCopyOnWriteMap<K, V, Map<K, V>> { private static final long serialVersionUID = 7935514534647505917L;
Get a Builder for a CopyOnWriteMap instance.
Type parameters:
  • <K> – key type
  • <V> – value type
Returns:a fresh builder
/** * Get a {@link Builder} for a CopyOnWriteMap instance. * * @param <K> key type * @param <V> value type * @return a fresh builder */
public static <K, V> Builder<K, V> builder() { return new Builder<K, V>(); }
Build a CopyOnWriteMap and specify all the options.
Type parameters:
  • <K> – key type
  • <V> – value type
/** * Build a {@link CopyOnWriteMap} and specify all the options. * * @param <K> key type * @param <V> value type */
public static class Builder<K, V> { private View.Type viewType = View.Type.STABLE; private final Map<K, V> initialValues = new HashMap<K, V>(); Builder() { }
Views are stable (fixed in time) and unmodifiable.
/** * Views are stable (fixed in time) and unmodifiable. */
public Builder<K, V> stableViews() { viewType = View.Type.STABLE; return this; }
Views are live (reflecting concurrent updates) and mutator methods are supported.
/** * Views are live (reflecting concurrent updates) and mutator methods are supported. */
public Builder<K, V> addAll(final Map<? extends K, ? extends V> values) { initialValues.putAll(values); return this; }
Views are live (reflecting concurrent updates) and mutator methods are supported.
/** * Views are live (reflecting concurrent updates) and mutator methods are supported. */
public Builder<K, V> liveViews() { viewType = View.Type.LIVE; return this; } public CopyOnWriteMap<K, V> newHashMap() { return new Hash<K, V>(initialValues, viewType); } public CopyOnWriteMap<K, V> newLinkedMap() { return new Linked<K, V>(initialValues, viewType); } }

Creates a new CopyOnWriteMap with an underlying HashMap.

This map has stable views.

/** * <p> Creates a new {@link CopyOnWriteMap} with an underlying {@link HashMap}. </p> * * <p>This map has {@link View.Type#STABLE stable} views.</p> */
public static <K, V> CopyOnWriteMap<K, V> newHashMap() { Builder<K, V> builder = builder(); return builder.newHashMap(); }

Creates a new CopyOnWriteMap with an underlying HashMap using the supplied map as the initial values.

This map has stable views.

/** * <p> Creates a new {@link CopyOnWriteMap} with an underlying {@link HashMap} using the supplied map as the initial values. </p> * * <p>This map has {@link View.Type#STABLE stable} views.</p> */
public static <K, V> CopyOnWriteMap<K, V> newHashMap(final Map<? extends K, ? extends V> map) { Builder<K, V> builder = builder(); return builder.addAll(map).newHashMap(); }

Creates a new CopyOnWriteMap with an underlying LinkedHashMap. Iterators for this map will be return elements in insertion order.

This map has stable views.

/** * <p>Creates a new {@link CopyOnWriteMap} with an underlying {@link java.util.LinkedHashMap}. Iterators for this map will be return * elements in insertion order. </p> * * <p>This map has {@link View.Type#STABLE stable} views.</p> */
public static <K, V> CopyOnWriteMap<K, V> newLinkedMap() { Builder<K, V> builder = builder(); return builder.newLinkedMap(); }

Creates a new CopyOnWriteMap with an underlying LinkedHashMap using the supplied map as the initial values. Iterators for this map will be return elements in insertion order.

This map has stable views.

/** * <p>Creates a new {@link CopyOnWriteMap} with an underlying {@link java.util.LinkedHashMap} using the supplied map as the initial * values. Iterators for this map will be return elements in insertion order. </p> * * <p>This map has {@link View.Type#STABLE stable} views.</p> */
public static <K, V> CopyOnWriteMap<K, V> newLinkedMap(final Map<? extends K, ? extends V> map) { Builder<K, V> builder = builder(); return builder.addAll(map).newLinkedMap(); } // // constructors //
Create a new CopyOnWriteMap with the supplied Map to initialize the values.
Params:
  • map – the initial map to initialize with
/** * Create a new {@link CopyOnWriteMap} with the supplied {@link Map} to initialize the values. * * @param map the initial map to initialize with */
protected CopyOnWriteMap(final Map<? extends K, ? extends V> map) { this(map, View.Type.LIVE); }
Create a new empty CopyOnWriteMap.
/** * Create a new empty {@link CopyOnWriteMap}. */
protected CopyOnWriteMap() { this(Collections.<K, V>emptyMap(), View.Type.LIVE); }
Create a new CopyOnWriteMap with the supplied Map to initialize the values. This map may be optionally modified using any of the key, entry or value views
Params:
  • map – the initial map to initialize with
/** * Create a new {@link CopyOnWriteMap} with the supplied {@link Map} to initialize the values. This map may be optionally modified using * any of the key, entry or value views * * @param map the initial map to initialize with */
protected CopyOnWriteMap(final Map<? extends K, ? extends V> map, final View.Type viewType) { super(map, viewType); }
Create a new empty CopyOnWriteMap. This map may be optionally modified using any of the key, entry or value views
/** * Create a new empty {@link CopyOnWriteMap}. This map may be optionally modified using any of the key, entry or value views */
protected CopyOnWriteMap(final View.Type viewType) { super(Collections.<K, V>emptyMap(), viewType); } @Override // @GuardedBy("internal-lock") protected abstract <N extends Map<? extends K, ? extends V>> Map<K, V> copy(N map); // // inner classes //
Uses HashMap instances as its internal storage.
/** * Uses {@link HashMap} instances as its internal storage. */
static class Hash<K, V> extends CopyOnWriteMap<K, V> { private static final long serialVersionUID = 5221824943734164497L; Hash(final Map<? extends K, ? extends V> map, final View.Type viewType) { super(map, viewType); } @Override public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) { return new HashMap<K, V>(map); } }
Uses LinkedHashMap instances as its internal storage.
/** * Uses {@link java.util.LinkedHashMap} instances as its internal storage. */
static class Linked<K, V> extends CopyOnWriteMap<K, V> { private static final long serialVersionUID = -8659999465009072124L; Linked(final Map<? extends K, ? extends V> map, final View.Type viewType) { super(map, viewType); } @Override public <N extends Map<? extends K, ? extends V>> Map<K, V> copy(final N map) { return new LinkedHashMap<K, V>(map); } } }