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