001/* 002 * Copyright (C) 2011 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.util.concurrent; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.Beta; 022import com.google.common.annotations.GwtCompatible; 023import com.google.errorprone.annotations.CanIgnoreReturnValue; 024import java.io.Serializable; 025import java.util.Collections; 026import java.util.Map; 027import java.util.concurrent.ConcurrentHashMap; 028import java.util.concurrent.atomic.AtomicBoolean; 029import java.util.concurrent.atomic.AtomicLong; 030import java.util.function.LongBinaryOperator; 031import java.util.function.LongUnaryOperator; 032 033/** 034 * A map containing {@code long} values that can be atomically updated. While writes to a 035 * traditional {@code Map} rely on {@code put(K, V)}, the typical mechanism for writing to this map 036 * is {@code addAndGet(K, long)}, which adds a {@code long} to the value currently associated with 037 * {@code K}. If a key has not yet been associated with a value, its implicit value is zero. 038 * 039 * <p>Most methods in this class treat absent values and zero values identically, as individually 040 * documented. Exceptions to this are {@link #containsKey}, {@link #size}, {@link #isEmpty}, {@link 041 * #asMap}, and {@link #toString}. 042 * 043 * <p>Instances of this class may be used by multiple threads concurrently. All operations are 044 * atomic unless otherwise noted. 045 * 046 * <p><b>Note:</b> If your values are always positive and less than 2^31, you may wish to use a 047 * {@link com.google.common.collect.Multiset} such as {@link 048 * com.google.common.collect.ConcurrentHashMultiset} instead. 049 * 050 * <p><b>Warning:</b> Unlike {@code Multiset}, entries whose values are zero are not automatically 051 * removed from the map. Instead they must be removed manually with {@link #removeAllZeros}. 052 * 053 * @author Charles Fry 054 * @since 11.0 055 */ 056@GwtCompatible 057public final class AtomicLongMap<K> implements Serializable { 058 private final ConcurrentHashMap<K, Long> map; 059 060 private AtomicLongMap(ConcurrentHashMap<K, Long> map) { 061 this.map = checkNotNull(map); 062 } 063 064 /** Creates an {@code AtomicLongMap}. */ 065 public static <K> AtomicLongMap<K> create() { 066 return new AtomicLongMap<K>(new ConcurrentHashMap<>()); 067 } 068 069 /** Creates an {@code AtomicLongMap} with the same mappings as the specified {@code Map}. */ 070 public static <K> AtomicLongMap<K> create(Map<? extends K, ? extends Long> m) { 071 AtomicLongMap<K> result = create(); 072 result.putAll(m); 073 return result; 074 } 075 076 /** 077 * Returns the value associated with {@code key}, or zero if there is no value associated with 078 * {@code key}. 079 */ 080 public long get(K key) { 081 return map.getOrDefault(key, 0L); 082 } 083 084 /** 085 * Increments by one the value currently associated with {@code key}, and returns the new value. 086 */ 087 @CanIgnoreReturnValue 088 public long incrementAndGet(K key) { 089 return addAndGet(key, 1); 090 } 091 092 /** 093 * Decrements by one the value currently associated with {@code key}, and returns the new value. 094 */ 095 @CanIgnoreReturnValue 096 public long decrementAndGet(K key) { 097 return addAndGet(key, -1); 098 } 099 100 /** 101 * Adds {@code delta} to the value currently associated with {@code key}, and returns the new 102 * value. 103 */ 104 @CanIgnoreReturnValue 105 public long addAndGet(K key, long delta) { 106 return accumulateAndGet(key, delta, Long::sum); 107 } 108 109 /** 110 * Increments by one the value currently associated with {@code key}, and returns the old value. 111 */ 112 @CanIgnoreReturnValue 113 public long getAndIncrement(K key) { 114 return getAndAdd(key, 1); 115 } 116 117 /** 118 * Decrements by one the value currently associated with {@code key}, and returns the old value. 119 */ 120 @CanIgnoreReturnValue 121 public long getAndDecrement(K key) { 122 return getAndAdd(key, -1); 123 } 124 125 /** 126 * Adds {@code delta} to the value currently associated with {@code key}, and returns the old 127 * value. 128 */ 129 @CanIgnoreReturnValue 130 public long getAndAdd(K key, long delta) { 131 return getAndAccumulate(key, delta, Long::sum); 132 } 133 134 /** 135 * Updates the value currently associated with {@code key} with the specified function, and 136 * returns the new value. If there is not currently a value associated with {@code key}, the 137 * function is applied to {@code 0L}. 138 * 139 * @since 21.0 140 */ 141 @CanIgnoreReturnValue 142 public long updateAndGet(K key, LongUnaryOperator updaterFunction) { 143 checkNotNull(updaterFunction); 144 return map.compute( 145 key, (k, value) -> updaterFunction.applyAsLong((value == null) ? 0L : value.longValue())); 146 } 147 148 /** 149 * Updates the value currently associated with {@code key} with the specified function, and 150 * returns the old value. If there is not currently a value associated with {@code key}, the 151 * function is applied to {@code 0L}. 152 * 153 * @since 21.0 154 */ 155 @CanIgnoreReturnValue 156 public long getAndUpdate(K key, LongUnaryOperator updaterFunction) { 157 checkNotNull(updaterFunction); 158 AtomicLong holder = new AtomicLong(); 159 map.compute( 160 key, 161 (k, value) -> { 162 long oldValue = (value == null) ? 0L : value.longValue(); 163 holder.set(oldValue); 164 return updaterFunction.applyAsLong(oldValue); 165 }); 166 return holder.get(); 167 } 168 169 /** 170 * Updates the value currently associated with {@code key} by combining it with {@code x} via the 171 * specified accumulator function, returning the new value. The previous value associated with 172 * {@code key} (or zero, if there is none) is passed as the first argument to {@code 173 * accumulatorFunction}, and {@code x} is passed as the second argument. 174 * 175 * @since 21.0 176 */ 177 @CanIgnoreReturnValue 178 public long accumulateAndGet(K key, long x, LongBinaryOperator accumulatorFunction) { 179 checkNotNull(accumulatorFunction); 180 return updateAndGet(key, oldValue -> accumulatorFunction.applyAsLong(oldValue, x)); 181 } 182 183 /** 184 * Updates the value currently associated with {@code key} by combining it with {@code x} via the 185 * specified accumulator function, returning the old value. The previous value associated with 186 * {@code key} (or zero, if there is none) is passed as the first argument to {@code 187 * accumulatorFunction}, and {@code x} is passed as the second argument. 188 * 189 * @since 21.0 190 */ 191 @CanIgnoreReturnValue 192 public long getAndAccumulate(K key, long x, LongBinaryOperator accumulatorFunction) { 193 checkNotNull(accumulatorFunction); 194 return getAndUpdate(key, oldValue -> accumulatorFunction.applyAsLong(oldValue, x)); 195 } 196 197 /** 198 * Associates {@code newValue} with {@code key} in this map, and returns the value previously 199 * associated with {@code key}, or zero if there was no such value. 200 */ 201 @CanIgnoreReturnValue 202 public long put(K key, long newValue) { 203 return getAndUpdate(key, x -> newValue); 204 } 205 206 /** 207 * Copies all of the mappings from the specified map to this map. The effect of this call is 208 * equivalent to that of calling {@code put(k, v)} on this map once for each mapping from key 209 * {@code k} to value {@code v} in the specified map. The behavior of this operation is undefined 210 * if the specified map is modified while the operation is in progress. 211 */ 212 public void putAll(Map<? extends K, ? extends Long> m) { 213 m.forEach(this::put); 214 } 215 216 /** 217 * Removes and returns the value associated with {@code key}. If {@code key} is not in the map, 218 * this method has no effect and returns zero. 219 */ 220 @CanIgnoreReturnValue 221 public long remove(K key) { 222 Long result = map.remove(key); 223 return (result == null) ? 0L : result.longValue(); 224 } 225 226 /** 227 * Atomically remove {@code key} from the map iff its associated value is 0. 228 * 229 * @since 20.0 230 */ 231 @Beta 232 @CanIgnoreReturnValue 233 public boolean removeIfZero(K key) { 234 return remove(key, 0); 235 } 236 237 /** 238 * Removes all mappings from this map whose values are zero. 239 * 240 * <p>This method is not atomic: the map may be visible in intermediate states, where some of the 241 * zero values have been removed and others have not. 242 */ 243 public void removeAllZeros() { 244 map.values().removeIf(x -> x == 0); 245 } 246 247 /** 248 * Returns the sum of all values in this map. 249 * 250 * <p>This method is not atomic: the sum may or may not include other concurrent operations. 251 */ 252 public long sum() { 253 return map.values().stream().mapToLong(Long::longValue).sum(); 254 } 255 256 private transient Map<K, Long> asMap; 257 258 /** Returns a live, read-only view of the map backing this {@code AtomicLongMap}. */ 259 public Map<K, Long> asMap() { 260 Map<K, Long> result = asMap; 261 return (result == null) ? asMap = createAsMap() : result; 262 } 263 264 private Map<K, Long> createAsMap() { 265 return Collections.unmodifiableMap(map); 266 } 267 268 /** Returns true if this map contains a mapping for the specified key. */ 269 public boolean containsKey(Object key) { 270 return map.containsKey(key); 271 } 272 273 /** 274 * Returns the number of key-value mappings in this map. If the map contains more than {@code 275 * Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}. 276 */ 277 public int size() { 278 return map.size(); 279 } 280 281 /** Returns {@code true} if this map contains no key-value mappings. */ 282 public boolean isEmpty() { 283 return map.isEmpty(); 284 } 285 286 /** 287 * Removes all of the mappings from this map. The map will be empty after this call returns. 288 * 289 * <p>This method is not atomic: the map may not be empty after returning if there were concurrent 290 * writes. 291 */ 292 public void clear() { 293 map.clear(); 294 } 295 296 @Override 297 public String toString() { 298 return map.toString(); 299 } 300 301 /** 302 * If {@code key} is not already associated with a value or if {@code key} is associated with 303 * zero, associate it with {@code newValue}. Returns the previous value associated with {@code 304 * key}, or zero if there was no mapping for {@code key}. 305 */ 306 long putIfAbsent(K key, long newValue) { 307 AtomicBoolean noValue = new AtomicBoolean(false); 308 Long result = 309 map.compute( 310 key, 311 (k, oldValue) -> { 312 if (oldValue == null || oldValue == 0) { 313 noValue.set(true); 314 return newValue; 315 } else { 316 return oldValue; 317 } 318 }); 319 return noValue.get() ? 0L : result.longValue(); 320 } 321 322 /** 323 * If {@code (key, expectedOldValue)} is currently in the map, this method replaces {@code 324 * expectedOldValue} with {@code newValue} and returns true; otherwise, this method returns false. 325 * 326 * <p>If {@code expectedOldValue} is zero, this method will succeed if {@code (key, zero)} is 327 * currently in the map, or if {@code key} is not in the map at all. 328 */ 329 boolean replace(K key, long expectedOldValue, long newValue) { 330 if (expectedOldValue == 0L) { 331 return putIfAbsent(key, newValue) == 0L; 332 } else { 333 return map.replace(key, expectedOldValue, newValue); 334 } 335 } 336 337 /** 338 * If {@code (key, value)} is currently in the map, this method removes it and returns true; 339 * otherwise, this method returns false. 340 */ 341 boolean remove(K key, long value) { 342 return map.remove(key, value); 343 } 344}