Coverage Summary for Class: MapIteratorCache (com.google.common.graph)
| Class | Method, % | Line, % |
|---|---|---|
| MapIteratorCache | 0% (0/11) | 0% (0/26) |
| MapIteratorCache$1 | 0% (0/4) | 0% (0/5) |
| MapIteratorCache$1$1 | 0% (0/3) | 0% (0/5) |
| Total | 0% (0/18) | 0% (0/36) |
1 /* 2 * Copyright (C) 2016 The Guava Authors 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.google.common.graph; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import com.google.common.collect.UnmodifiableIterator; 22 import com.google.errorprone.annotations.CanIgnoreReturnValue; 23 import java.util.AbstractSet; 24 import java.util.Iterator; 25 import java.util.Map; 26 import java.util.Map.Entry; 27 import java.util.Set; 28 import javax.annotation.CheckForNull; 29 30 /** 31 * A map-like data structure that wraps a backing map and caches values while iterating through 32 * {@link #unmodifiableKeySet()}. By design, the cache is cleared when this structure is mutated. If 33 * this structure is never mutated, it provides a thread-safe view of the backing map. 34 * 35 * <p>The {@link MapIteratorCache} assumes ownership of the backing map, and cannot guarantee 36 * correctness in the face of external mutations to the backing map. As such, it is <b>strongly</b> 37 * recommended that the caller does not persist a reference to the backing map (unless the backing 38 * map is immutable). 39 * 40 * <p>This class is tailored toward use cases in common.graph. It is *NOT* a general purpose map. 41 * 42 * @author James Sexton 43 */ 44 @ElementTypesAreNonnullByDefault 45 class MapIteratorCache<K, V> { 46 private final Map<K, V> backingMap; 47 48 /* 49 * Per JDK: "the behavior of a map entry is undefined if the backing map has been modified after 50 * the entry was returned by the iterator, except through the setValue operation on the map entry" 51 * As such, this field must be cleared before every map mutation. 52 * 53 * Note about volatile: volatile doesn't make it safe to read from a mutable graph in one thread 54 * while writing to it in another. All it does is help with _reading_ from multiple threads 55 * concurrently. For more information, see AbstractNetworkTest.concurrentIteration. 56 */ 57 @CheckForNull private transient volatile Entry<K, V> cacheEntry; 58 59 MapIteratorCache(Map<K, V> backingMap) { 60 this.backingMap = checkNotNull(backingMap); 61 } 62 63 @CanIgnoreReturnValue 64 @CheckForNull 65 final V put(K key, V value) { 66 checkNotNull(key); 67 checkNotNull(value); 68 clearCache(); 69 return backingMap.put(key, value); 70 } 71 72 @CanIgnoreReturnValue 73 @CheckForNull 74 final V remove(Object key) { 75 checkNotNull(key); 76 clearCache(); 77 return backingMap.remove(key); 78 } 79 80 final void clear() { 81 clearCache(); 82 backingMap.clear(); 83 } 84 85 @CheckForNull 86 V get(Object key) { 87 checkNotNull(key); 88 V value = getIfCached(key); 89 // TODO(cpovirk): Switch back to a ternary once our checker allows it. 90 if (value == null) { 91 return getWithoutCaching(key); 92 } else { 93 return value; 94 } 95 } 96 97 @CheckForNull 98 final V getWithoutCaching(Object key) { 99 checkNotNull(key); 100 return backingMap.get(key); 101 } 102 103 final boolean containsKey(@CheckForNull Object key) { 104 return getIfCached(key) != null || backingMap.containsKey(key); 105 } 106 107 final Set<K> unmodifiableKeySet() { 108 return new AbstractSet<K>() { 109 @Override 110 public UnmodifiableIterator<K> iterator() { 111 final Iterator<Entry<K, V>> entryIterator = backingMap.entrySet().iterator(); 112 113 return new UnmodifiableIterator<K>() { 114 @Override 115 public boolean hasNext() { 116 return entryIterator.hasNext(); 117 } 118 119 @Override 120 public K next() { 121 Entry<K, V> entry = entryIterator.next(); // store local reference for thread-safety 122 cacheEntry = entry; 123 return entry.getKey(); 124 } 125 }; 126 } 127 128 @Override 129 public int size() { 130 return backingMap.size(); 131 } 132 133 @Override 134 public boolean contains(@CheckForNull Object key) { 135 return containsKey(key); 136 } 137 }; 138 } 139 140 // Internal methods (package-visible, but treat as only subclass-visible) 141 142 @CheckForNull 143 V getIfCached(@CheckForNull Object key) { 144 Entry<K, V> entry = cacheEntry; // store local reference for thread-safety 145 146 // Check cache. We use == on purpose because it's cheaper and a cache miss is ok. 147 if (entry != null && entry.getKey() == key) { 148 return entry.getValue(); 149 } 150 return null; 151 } 152 153 void clearCache() { 154 cacheEntry = null; 155 } 156 }