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}