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 }