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}, 041 * {@link #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 048 * {@link com.google.common.collect.ConcurrentHashMultiset} instead. 049 * 050 * <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 /** 065 * Creates an {@code AtomicLongMap}. 066 */ 067 public static <K> AtomicLongMap<K> create() { 068 return new AtomicLongMap<K>(new ConcurrentHashMap<>()); 069 } 070 071 /** 072 * Creates an {@code AtomicLongMap} with the same mappings as the specified {@code Map}. 073 */ 074 public static <K> AtomicLongMap<K> create(Map<? extends K, ? extends Long> m) { 075 AtomicLongMap<K> result = create(); 076 result.putAll(m); 077 return result; 078 } 079 080 /** 081 * Returns the value associated with {@code key}, or zero if there is no value associated with 082 * {@code key}. 083 */ 084 public long get(K key) { 085 return map.getOrDefault(key, 0L); 086 } 087 088 /** 089 * Increments by one the value currently associated with {@code key}, and returns the new value. 090 */ 091 @CanIgnoreReturnValue 092 public long incrementAndGet(K key) { 093 return addAndGet(key, 1); 094 } 095 096 /** 097 * Decrements by one the value currently associated with {@code key}, and returns the new value. 098 */ 099 @CanIgnoreReturnValue 100 public long decrementAndGet(K key) { 101 return addAndGet(key, -1); 102 } 103 104 /** 105 * Adds {@code delta} to the value currently associated with {@code key}, and returns the new 106 * value. 107 */ 108 @CanIgnoreReturnValue 109 public long addAndGet(K key, long delta) { 110 return accumulateAndGet(key, delta, Long::sum); 111 } 112 113 /** 114 * Increments by one the value currently associated with {@code key}, and returns the old value. 115 */ 116 @CanIgnoreReturnValue 117 public long getAndIncrement(K key) { 118 return getAndAdd(key, 1); 119 } 120 121 /** 122 * Decrements by one the value currently associated with {@code key}, and returns the old value. 123 */ 124 @CanIgnoreReturnValue 125 public long getAndDecrement(K key) { 126 return getAndAdd(key, -1); 127 } 128 129 /** 130 * Adds {@code delta} to the value currently associated with {@code key}, and returns the old 131 * value. 132 */ 133 @CanIgnoreReturnValue 134 public long getAndAdd(K key, long delta) { 135 return getAndAccumulate(key, delta, Long::sum); 136 } 137 138 /** 139 * Updates the value currently associated with {@code key} with the specified function, 140 * and returns the new value. If there is not currently a value associated with {@code key}, 141 * the function is applied to {@code 0L}. 142 * 143 * @since 21.0 144 */ 145 @CanIgnoreReturnValue 146 public long updateAndGet(K key, LongUnaryOperator updaterFunction) { 147 checkNotNull(updaterFunction); 148 return map.compute( 149 key, (k, value) -> updaterFunction.applyAsLong((value == null) ? 0L : value.longValue())); 150 } 151 152 /** 153 * Updates the value currently associated with {@code key} with the specified function, 154 * and returns the old value. If there is not currently a value associated with {@code key}, 155 * the function is applied to {@code 0L}. 156 * 157 * @since 21.0 158 */ 159 @CanIgnoreReturnValue 160 public long getAndUpdate(K key, LongUnaryOperator updaterFunction) { 161 checkNotNull(updaterFunction); 162 AtomicLong holder = new AtomicLong(); 163 map.compute( 164 key, 165 (k, value) -> { 166 long oldValue = (value == null) ? 0L : value.longValue(); 167 holder.set(oldValue); 168 return updaterFunction.applyAsLong(oldValue); 169 }); 170 return holder.get(); 171 } 172 173 /** 174 * Updates the value currently associated with {@code key} by combining it with {@code x} 175 * via the specified accumulator function, returning the new value. The previous value 176 * associated with {@code key} (or zero, if there is none) is passed as the first argument 177 * to {@code accumulatorFunction}, and {@code x} is passed as the second argument. 178 * 179 * @since 21.0 180 */ 181 @CanIgnoreReturnValue 182 public long accumulateAndGet(K key, long x, LongBinaryOperator accumulatorFunction) { 183 checkNotNull(accumulatorFunction); 184 return updateAndGet(key, oldValue -> accumulatorFunction.applyAsLong(oldValue, x)); 185 } 186 187 /** 188 * Updates the value currently associated with {@code key} by combining it with {@code x} 189 * via the specified accumulator function, returning the old value. The previous value 190 * associated with {@code key} (or zero, if there is none) is passed as the first argument 191 * to {@code accumulatorFunction}, and {@code x} is passed as the second argument. 192 * 193 * @since 21.0 194 */ 195 @CanIgnoreReturnValue 196 public long getAndAccumulate(K key, long x, LongBinaryOperator accumulatorFunction) { 197 checkNotNull(accumulatorFunction); 198 return getAndUpdate(key, oldValue -> accumulatorFunction.applyAsLong(oldValue, x)); 199 } 200 201 /** 202 * Associates {@code newValue} with {@code key} in this map, and returns the value previously 203 * associated with {@code key}, or zero if there was no such value. 204 */ 205 @CanIgnoreReturnValue 206 public long put(K key, long newValue) { 207 return getAndUpdate(key, x -> newValue); 208 } 209 210 /** 211 * Copies all of the mappings from the specified map to this map. The effect of this call is 212 * equivalent to that of calling {@code put(k, v)} on this map once for each mapping from key 213 * {@code k} to value {@code v} in the specified map. The behavior of this operation is undefined 214 * if the specified map is modified while the operation is in progress. 215 */ 216 public void putAll(Map<? extends K, ? extends Long> m) { 217 m.forEach(this::put); 218 } 219 220 /** 221 * Removes and returns the value associated with {@code key}. If {@code key} is not 222 * in the map, this method has no effect and returns zero. 223 */ 224 @CanIgnoreReturnValue 225 public long remove(K key) { 226 Long result = map.remove(key); 227 return (result == null) ? 0L : result.longValue(); 228 } 229 230 /** 231 * Atomically remove {@code key} from the map iff its associated value is 0. 232 * 233 * @since 20.0 234 */ 235 @Beta 236 @CanIgnoreReturnValue 237 public boolean removeIfZero(K key) { 238 return remove(key, 0); 239 } 240 241 /** 242 * Removes all mappings from this map whose values are zero. 243 * 244 * <p>This method is not atomic: the map may be visible in intermediate states, where some 245 * of the zero values have been removed and others have not. 246 */ 247 public void removeAllZeros() { 248 map.values().removeIf(x -> x == 0); 249 } 250 251 /** 252 * Returns the sum of all values in this map. 253 * 254 * <p>This method is not atomic: the sum may or may not include other concurrent operations. 255 */ 256 public long sum() { 257 return map.values().stream().mapToLong(Long::longValue).sum(); 258 } 259 260 private transient Map<K, Long> asMap; 261 262 /** 263 * Returns a live, read-only view of the map backing this {@code AtomicLongMap}. 264 */ 265 public Map<K, Long> asMap() { 266 Map<K, Long> result = asMap; 267 return (result == null) ? asMap = createAsMap() : result; 268 } 269 270 private Map<K, Long> createAsMap() { 271 return Collections.unmodifiableMap(map); 272 } 273 274 /** 275 * Returns true if this map contains a mapping for the specified key. 276 */ 277 public boolean containsKey(Object key) { 278 return map.containsKey(key); 279 } 280 281 /** 282 * Returns the number of key-value mappings in this map. If the map contains more than 283 * {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}. 284 */ 285 public int size() { 286 return map.size(); 287 } 288 289 /** 290 * Returns {@code true} if this map contains no key-value mappings. 291 */ 292 public boolean isEmpty() { 293 return map.isEmpty(); 294 } 295 296 /** 297 * Removes all of the mappings from this map. The map will be empty after this call returns. 298 * 299 * <p>This method is not atomic: the map may not be empty after returning if there were concurrent 300 * writes. 301 */ 302 public void clear() { 303 map.clear(); 304 } 305 306 @Override 307 public String toString() { 308 return map.toString(); 309 } 310 311 /** 312 * If {@code key} is not already associated with a value or if {@code key} is associated with 313 * zero, associate it with {@code newValue}. Returns the previous value associated with 314 * {@code key}, or zero if there was no mapping for {@code key}. 315 */ 316 long putIfAbsent(K key, long newValue) { 317 AtomicBoolean noValue = new AtomicBoolean(false); 318 Long result = 319 map.compute( 320 key, 321 (k, oldValue) -> { 322 if (oldValue == null || oldValue == 0) { 323 noValue.set(true); 324 return newValue; 325 } else { 326 return oldValue; 327 } 328 }); 329 return noValue.get() ? 0L : result.longValue(); 330 } 331 332 /** 333 * If {@code (key, expectedOldValue)} is currently in the map, this method replaces 334 * {@code expectedOldValue} with {@code newValue} and returns true; otherwise, this method 335 * returns false. 336 * 337 * <p>If {@code expectedOldValue} is zero, this method will succeed if {@code (key, zero)} 338 * is currently in the map, or if {@code key} is not in the map at all. 339 */ 340 boolean replace(K key, long expectedOldValue, long newValue) { 341 if (expectedOldValue == 0L) { 342 return putIfAbsent(key, newValue) == 0L; 343 } else { 344 return map.replace(key, expectedOldValue, newValue); 345 } 346 } 347 348 /** 349 * If {@code (key, value)} is currently in the map, this method removes it and returns 350 * true; otherwise, this method returns false. 351 */ 352 boolean remove(K key, long value) { 353 return map.remove(key, value); 354 } 355}