2 * (C) Copyright 2016 Pantheon Technologies, s.r.o. and others.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.opendaylight.yangtools.triemap;
18 import static java.util.Objects.requireNonNull;
19 import static org.opendaylight.yangtools.triemap.LookupResult.RESTART;
21 import com.google.common.annotations.Beta;
22 import java.io.ObjectStreamException;
23 import java.io.Serializable;
24 import java.util.AbstractMap;
25 import java.util.Optional;
27 import java.util.concurrent.ConcurrentMap;
30 * This is a port of Scala's TrieMap class from the Scala Collections library. This implementation does not support
31 * null keys nor null values.
33 * @author Aleksandar Prokopec (original Scala implementation)
34 * @author Roman Levenstein (original Java 6 port)
35 * @author Robert Varga
37 * @param <K> the type of keys maintained by this map
38 * @param <V> the type of mapped values
41 public abstract class TrieMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K,V>, Serializable {
42 private static final long serialVersionUID = 1L;
44 private final Equivalence<? super K> equiv;
46 private AbstractEntrySet<K, V> entrySet;
47 private AbstractKeySet<K> keySet;
49 TrieMap(final Equivalence<? super K> equiv) {
53 public static <K, V> MutableTrieMap<K, V> create() {
54 return new MutableTrieMap<>(Equivalence.equals());
58 * Returns a snapshot of this TrieMap. This operation is lock-free and
59 * linearizable. Modification operations on this Map and the returned one
60 * are isolated from each other.
63 * The snapshot is lazily updated - the first time some branch in the
64 * snapshot or this TrieMap are accessed, they are rewritten. This means
65 * that the work of rebuilding both the snapshot and this TrieMap is
66 * distributed across all the threads doing updates or accesses subsequent
67 * to the snapshot creation.
69 * @return A read-write TrieMap containing the contents of this map.
71 public abstract TrieMap<K, V> mutableSnapshot();
74 * Returns a read-only snapshot of this TrieMap. This operation is lock-free
78 * The snapshot is lazily updated - the first time some branch of this
79 * TrieMap are accessed, it is rewritten. The work of creating the snapshot
80 * is thus distributed across subsequent updates and accesses on this
81 * TrieMap by all threads. Note that the snapshot itself is never rewritten
82 * unlike when calling {@link #mutableSnapshot()}, but the obtained snapshot
86 * This method is used by other methods such as `size` and `iterator`.
88 * @return A read-only TrieMap containing the contents of this map.
90 public abstract ImmutableTrieMap<K, V> immutableSnapshot();
93 public final boolean containsKey(final Object key) {
94 return get(key) != null;
98 public final boolean containsValue(final Object value) {
99 return super.containsValue(requireNonNull(value));
103 public final Set<Entry<K, V>> entrySet() {
104 final AbstractEntrySet<K, V> ret;
105 return (ret = entrySet) != null ? ret : (entrySet = createEntrySet());
109 public final Set<K> keySet() {
110 final AbstractKeySet<K> ret;
111 return (ret = keySet) != null ? ret : (keySet = createKeySet());
115 public final V get(final Object key) {
116 @SuppressWarnings("unchecked")
117 final K k = (K) requireNonNull(key);
118 return lookuphc(k, computeHash(k));
122 public abstract void clear();
125 public abstract V put(K key, V value);
128 public abstract V putIfAbsent(K key, V value);
131 public abstract V remove(Object key);
134 public abstract boolean remove(Object key, Object value);
137 public abstract boolean replace(K key, V oldValue, V newValue);
140 public abstract V replace(K key, V value);
143 public abstract int size();
145 /* internal methods implemented by subclasses */
147 abstract AbstractEntrySet<K, V> createEntrySet();
149 abstract AbstractKeySet<K> createKeySet();
151 abstract boolean isReadOnly();
153 abstract INode<K, V> RDCSS_READ_ROOT(boolean abort);
156 * Return an iterator over a TrieMap.
159 * If this is a read-only snapshot, it would return a read-only iterator.
162 * If it is the original TrieMap or a non-readonly snapshot, it would return
163 * an iterator that would allow for updates.
165 * @return An iterator.
167 abstract AbstractIterator<K, V> iterator();
169 /* internal methods provided for subclasses */
172 * Return an iterator over a TrieMap. This is a read-only iterator.
174 * @return A read-only iterator.
176 final ImmutableIterator<K, V> immutableIterator() {
177 return new ImmutableIterator<>(immutableSnapshot());
180 @SuppressWarnings("null")
181 static <V> V toNullable(final Optional<V> opt) {
182 return opt.orElse(null);
185 final int computeHash(final K key) {
186 return equiv.hash(key);
189 final Object writeReplace() throws ObjectStreamException {
190 return new SerializationProxy(immutableSnapshot(), isReadOnly());
193 /* package-protected utility methods */
195 final Equivalence<? super K> equiv() {
199 final INode<K, V> readRoot() {
200 return RDCSS_READ_ROOT(false);
203 // FIXME: abort = false by default
204 final INode<K, V> readRoot(final boolean abort) {
205 return RDCSS_READ_ROOT(abort);
208 final INode<K, V> RDCSS_READ_ROOT() {
209 return RDCSS_READ_ROOT(false);
212 final boolean equal(final K k1, final K k2) {
213 return equiv.equivalent(k1, k2);
216 /* private implementation methods */
218 @SuppressWarnings("unchecked")
219 private V lookuphc(final K key, final int hc) {
222 // Keep looping as long as RESTART is being indicated
223 res = RDCSS_READ_ROOT().recLookup(key, hc, 0, null, this);
224 } while (res == RESTART);