• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ben-manes / caffeine / #5612

11 Jul 2026 03:47PM UTC coverage: 71.292% (-28.7%) from 100.0%
#5612

push

github

ben-manes
Preserve timestamps on a raced same-instance refresh

The automatic refreshIfNeeded path checked for an equal-value reload
before the ABA/ownership check and returned without preserving the
timestamps, so a reload completing after a concurrent put(k,
sameInstance) did a full update — shifting the entry's expiration
forward by the reload's in-flight duration and stomping the write.
Mirror the explicit-refresh fix: check ownership first. An owned
reload installs the value (refreshing metadata even for a same
instance), while a raced reload preserves the write's timestamps and
only notifies REPLACED when the value differs.

2957 of 4134 branches covered (71.53%)

3 of 3 new or added lines in 1 file covered. (100.0%)

2408 existing lines in 53 files now uncovered.

5980 of 8388 relevant lines covered (71.29%)

0.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.31
/caffeine/src/main/java/com/github/benmanes/caffeine/cache/Caffeine.java
1
/*
2
 * Copyright 2014 Ben Manes. All Rights Reserved.
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
package com.github.benmanes.caffeine.cache;
17

18
import static java.util.Locale.US;
19
import static java.util.Objects.requireNonNull;
20

21
import java.lang.System.Logger;
22
import java.lang.System.Logger.Level;
23
import java.lang.ref.SoftReference;
24
import java.lang.ref.WeakReference;
25
import java.lang.reflect.Method;
26
import java.time.Duration;
27
import java.util.Arrays;
28
import java.util.Collection;
29
import java.util.ConcurrentModificationException;
30
import java.util.HashMap;
31
import java.util.IdentityHashMap;
32
import java.util.Map;
33
import java.util.concurrent.CompletableFuture;
34
import java.util.concurrent.ConcurrentHashMap;
35
import java.util.concurrent.Executor;
36
import java.util.concurrent.ForkJoinPool;
37
import java.util.concurrent.ScheduledThreadPoolExecutor;
38
import java.util.concurrent.TimeUnit;
39
import java.util.function.Supplier;
40

41
import org.jspecify.annotations.NullMarked;
42
import org.jspecify.annotations.Nullable;
43

44
import com.github.benmanes.caffeine.cache.Async.AsyncEvictionListener;
45
import com.github.benmanes.caffeine.cache.Async.AsyncExpiry;
46
import com.github.benmanes.caffeine.cache.Async.AsyncRemovalListener;
47
import com.github.benmanes.caffeine.cache.Async.AsyncWeigher;
48
import com.github.benmanes.caffeine.cache.stats.CacheStats;
49
import com.github.benmanes.caffeine.cache.stats.ConcurrentStatsCounter;
50
import com.github.benmanes.caffeine.cache.stats.StatsCounter;
51
import com.google.errorprone.annotations.CanIgnoreReturnValue;
52
import com.google.errorprone.annotations.FormatMethod;
53

54
/**
55
 * A builder of {@link Cache}, {@link LoadingCache}, {@link AsyncCache}, and
56
 * {@link AsyncLoadingCache} instances having a combination of the following features:
57
 * <ul>
58
 *   <li>automatic loading of entries into the cache, optionally asynchronously
59
 *   <li>size-based eviction when a maximum is exceeded based on frequency and recency
60
 *   <li>time-based expiration of entries, measured since last access or last write
61
 *   <li>asynchronously refresh when the first stale request for an entry occurs
62
 *   <li>keys automatically wrapped in {@linkplain WeakReference weak} references
63
 *   <li>values automatically wrapped in {@linkplain WeakReference weak} or
64
 *       {@linkplain SoftReference soft} references
65
 *   <li>writes propagated to an external resource
66
 *   <li>notification of evicted (or otherwise removed) entries
67
 *   <li>accumulation of cache access statistics
68
 * </ul>
69
 * <p>
70
 * These features are all optional; caches can be created using all or none of them. By default,
71
 * cache instances created by {@code Caffeine} will not perform any type of eviction.
72
 * <p>
73
 * Usage example:
74
 * {@snippet class=com.github.benmanes.caffeine.cache.Snippets region=builder lang=java}
75
 * <p>
76
 * The returned cache is implemented as a hash table with similar performance characteristics to
77
 * {@link ConcurrentHashMap}. The {@code asMap} view (and its collection views) have <i>weakly
78
 * consistent iterators</i>. This means that they are safe for concurrent use, but if other threads
79
 * modify the cache after the iterator is created, it is undefined which of these changes, if any,
80
 * are reflected in that iterator. These iterators never throw
81
 * {@link ConcurrentModificationException}.
82
 * <p>
83
 * <b>Note:</b> By default, the returned cache uses equality comparisons (the
84
 * {@link Object#equals equals} method) to determine equality for keys or values. However, if
85
 * {@link #weakKeys} was specified, the cache uses identity ({@code ==}) comparisons instead for
86
 * keys. Likewise, if {@link #weakValues} or {@link #softValues} was specified, the cache uses
87
 * identity comparisons for values.
88
 * <p>
89
 * Entries are automatically evicted from the cache when any of
90
 * {@linkplain #maximumSize(long) maximumSize}, {@linkplain #maximumWeight(long) maximumWeight},
91
 * {@linkplain #expireAfter(Expiry) expireAfter}, {@linkplain #expireAfterWrite expireAfterWrite},
92
 * {@linkplain #expireAfterAccess expireAfterAccess}, {@linkplain #weakKeys weakKeys},
93
 * {@linkplain #weakValues weakValues}, or {@linkplain #softValues softValues} are requested.
94
 * <p>
95
 * If {@linkplain #maximumSize(long) maximumSize} or {@linkplain #maximumWeight(long) maximumWeight}
96
 * is requested, entries may be evicted on each cache modification.
97
 * <p>
98
 * If {@linkplain #expireAfter(Expiry) expireAfter},
99
 * {@linkplain #expireAfterWrite expireAfterWrite}, or
100
 * {@linkplain #expireAfterAccess expireAfterAccess} is requested, then entries may be evicted on
101
 * each cache modification, on occasional cache accesses, or on calls to {@link Cache#cleanUp}. A
102
 * {@linkplain #scheduler(Scheduler)} may be specified to provide prompt removal of expired entries
103
 * rather than waiting until activity triggers the periodic maintenance. Expired entries may be
104
 * counted by {@link Cache#estimatedSize()}, but will never be visible to read or write operations.
105
 * <p>
106
 * If {@linkplain #weakKeys weakKeys}, {@linkplain #weakValues weakValues}, or
107
 * {@linkplain #softValues softValues} are requested, it is possible for a key or value present in
108
 * the cache to be reclaimed by the garbage collector. Entries with reclaimed keys or values may be
109
 * removed from the cache on each cache modification, on occasional cache accesses, or on calls to
110
 * {@link Cache#cleanUp}; such entries may be counted in {@link Cache#estimatedSize()}, but will
111
 * never be visible to read or write operations.
112
 * <p>
113
 * Certain cache configurations will result in the accrual of periodic maintenance tasks that
114
 * will be performed during write operations, or during occasional read operations in the absence of
115
 * writes. The {@link Cache#cleanUp} method of the returned cache will also perform maintenance, but
116
 * calling it should not be necessary with a high-throughput cache. Only caches built with
117
 * {@linkplain #maximumSize maximumSize}, {@linkplain #maximumWeight maximumWeight},
118
 * {@linkplain #expireAfter(Expiry) expireAfter}, {@linkplain #expireAfterWrite expireAfterWrite},
119
 * {@linkplain #expireAfterAccess expireAfterAccess}, {@linkplain #weakKeys weakKeys},
120
 * {@linkplain #weakValues weakValues}, or {@linkplain #softValues softValues} perform periodic
121
 * maintenance.
122
 * <p>
123
 * The caches produced by {@code Caffeine} are serializable when their configured components (such
124
 * as the {@code CacheLoader}, {@code Weigher}, {@code Expiry}, and listeners) are serializable, and
125
 * the deserialized caches retain those configuration properties. The executor, scheduler, and
126
 * statistics are runtime state rather than configuration; they are not serialized and revert to the
127
 * defaults. Note that the serialized form does <i>not</i> include cache contents but only
128
 * configuration.
129
 *
130
 * @author ben.manes@gmail.com (Ben Manes)
131
 * @param <K> the most general key type this builder will be able to create caches for. This is
132
 *     normally {@code Object} unless it is constrained by using a method like {@code
133
 *     #removalListener}
134
 * @param <V> the most general value type this builder will be able to create caches for. This is
135
 *     normally {@code Object} unless it is constrained by using a method like {@code
136
 *     #removalListener}
137
 */
138
@NullMarked
139
@SuppressWarnings({"JavadocDeclaration", "JavadocReference"})
140
public final class Caffeine<K, V> {
141
  static final Supplier<StatsCounter> ENABLED_STATS_COUNTER_SUPPLIER = ConcurrentStatsCounter::new;
1✔
142
  static final Logger logger = System.getLogger(Caffeine.class.getName());
1✔
143
  static final Duration MIN_DURATION = Duration.ofNanos(Long.MIN_VALUE);
1✔
144
  static final Duration MAX_DURATION = Duration.ofNanos(Long.MAX_VALUE);
1✔
145
  static final double DEFAULT_LOAD_FACTOR = 0.75;
146
  static final int DEFAULT_INITIAL_CAPACITY = 16;
147

148
  enum Strength { WEAK, SOFT }
1✔
149
  static final int UNSET_INT = -1;
150

151
  boolean strictParsing = true;
1✔
152
  boolean interner;
153

154
  long maximumSize = UNSET_INT;
1✔
155
  long maximumWeight = UNSET_INT;
1✔
156
  int initialCapacity = UNSET_INT;
1✔
157

158
  long expireAfterWriteNanos = UNSET_INT;
1✔
159
  long expireAfterAccessNanos = UNSET_INT;
1✔
160
  long refreshAfterWriteNanos = UNSET_INT;
1✔
161

162
  @Nullable RemovalListener<? super K, ? super V> evictionListener;
163
  @Nullable RemovalListener<? super K, ? super V> removalListener;
164
  @Nullable Supplier<StatsCounter> statsCounterSupplier;
165
  @Nullable Weigher<? super K, ? super V> weigher;
166
  @Nullable Expiry<? super K, ? super V> expiry;
167
  @Nullable Scheduler scheduler;
168
  @Nullable Executor executor;
169
  @Nullable Ticker ticker;
170

171
  @Nullable Strength keyStrength;
172
  @Nullable Strength valueStrength;
173

174
  private Caffeine() {}
1✔
175

176
  /** Ensures that the argument expression is true. */
177
  @FormatMethod
178
  static void requireArgument(boolean expression, String template, @Nullable Object... args) {
179
    if (!expression) {
1✔
180
      throw new IllegalArgumentException(String.format(US, template, args));
1✔
181
    }
182
  }
1✔
183

184
  /** Ensures that the argument expression is true. */
185
  static void requireArgument(boolean expression, String message) {
186
    if (!expression) {
1✔
187
      throw new IllegalArgumentException(message);
1✔
188
    }
189
  }
1✔
190

191
  /** Ensures that the argument expression is true. */
192
  static void requireArgument(boolean expression) {
193
    if (!expression) {
1✔
194
      throw new IllegalArgumentException();
1✔
195
    }
196
  }
1✔
197

198
  /** Ensures that the state expression is true. */
199
  static void requireState(boolean expression) {
200
    if (!expression) {
1✔
201
      throw new IllegalStateException();
1✔
202
    }
203
  }
1✔
204

205
  /** Ensures that the state expression is true. */
206
  @FormatMethod
207
  static void requireState(boolean expression, String template, @Nullable Object... args) {
208
    if (!expression) {
1✔
209
      throw new IllegalStateException(String.format(US, template, args));
1✔
210
    }
211
  }
1✔
212

213
  /** Returns the smallest power of two greater than or equal to {@code x}, else the maximum. */
214
  static int ceilingPowerOfTwo(int x) {
215
    // From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
216
    if (x > (1 << 30)) {
1✔
217
      return 1 << 30;
1✔
218
    }
219
    return 1 << -Integer.numberOfLeadingZeros(x - 1);
1✔
220
  }
221

222
  /** Returns the smallest power of two greater than or equal to {@code x}, else the maximum. */
223
  static long ceilingPowerOfTwo(long x) {
224
    // From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
225
    if (x > (1L << 62)) {
1!
UNCOV
226
      return 1L << 62;
×
227
    }
228
    return 1L << -Long.numberOfLeadingZeros(x - 1);
1✔
229
  }
230

231
  /**
232
   * Calculate initial capacity for {@link HashMap}-based classes, from expected size and default
233
   * load factor (0.75).
234
   *
235
   * @param numMappings the expected number of mappings
236
   * @return initial capacity for HashMap based classes
237
   */
238
  static int calculateHashMapCapacity(int numMappings) {
239
    return (int) Math.ceil(numMappings / DEFAULT_LOAD_FACTOR);
1✔
240
  }
241

242
  /**
243
   * Calculate initial capacity for {@link HashMap}-based classes, from expected size and default
244
   * load factor (0.75).
245
   *
246
   * @param iterable the expected number of mappings
247
   * @return initial capacity for HashMap based classes
248
   */
249
  static int calculateHashMapCapacity(Iterable<?> iterable) {
250
    return (iterable instanceof Collection)
1✔
251
        ? calculateHashMapCapacity(((Collection<?>) iterable).size())
1✔
252
        : DEFAULT_INITIAL_CAPACITY;
1✔
253
  }
254

255
  /**
256
   * Returns the number of nanoseconds of the given duration without throwing or overflowing.
257
   * <p>
258
   * Instead of throwing {@link ArithmeticException}, this method silently saturates to either
259
   * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing
260
   * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair.
261
   */
262
  static long toNanosSaturated(Duration duration) {
263
    return duration.isNegative()
1✔
264
        ? (duration.compareTo(MIN_DURATION) <= 0) ? Long.MIN_VALUE : duration.toNanos()
1✔
265
        : (duration.compareTo(MAX_DURATION) >= 0) ? Long.MAX_VALUE : duration.toNanos();
1✔
266
  }
267

268
  /** Returns whether the instance has implemented a method for enhanced functionality. */
269
  static boolean hasMethodOverride(Class<?> clazz,
270
      Object instance, String methodName, Class<?>... parameterTypes) {
271
    try {
272
      Method instanceMethod = instance.getClass().getMethod(methodName, parameterTypes);
1✔
273
      Method classMethod = clazz.getMethod(methodName, parameterTypes);
1✔
274
      return !instanceMethod.equals(classMethod);
1✔
UNCOV
275
    } catch (NoSuchMethodException | SecurityException e) {
×
UNCOV
276
      logger.log(Level.WARNING, "Cannot determine if {0} overrides {1}({2})",
×
UNCOV
277
          instance.getClass().getSimpleName(), methodName, Arrays.toString(parameterTypes), e);
×
UNCOV
278
      return false;
×
279
    }
280
  }
281

282
  /**
283
   * Constructs a new {@code Caffeine} instance with default settings, including strong keys, strong
284
   * values, and no automatic eviction of any kind.
285
   * <p>
286
   * Note that while this return type is {@code Caffeine<Object, Object>}, type parameters on the
287
   * {@link #build} methods allow you to create a cache of any key and value type desired.
288
   *
289
   * @return a new instance with default settings
290
   */
291
  public static Caffeine<Object, Object> newBuilder() {
292
    return new Caffeine<>();
1✔
293
  }
294

295
  /** Returns a cache that is optimized for weak reference interning (see {@link Interner}). */
296
  static <K> BoundedLocalCache<K, Boolean> newWeakInterner() {
297
    var builder = new Caffeine<K, Boolean>().executor(Runnable::run).weakKeys();
1✔
298
    builder.interner = true;
1✔
299
    return LocalCacheFactory.newBoundedLocalCache(builder,
1✔
300
        /* cacheLoader= */ null, /* isAsync= */ false);
301
  }
302

303
  /**
304
   * Constructs a new {@code Caffeine} instance with the settings specified in {@code spec}.
305
   *
306
   * @param spec the specification to build from
307
   * @return a new instance with the specification's settings
308
   */
309
  public static Caffeine<Object, Object> from(CaffeineSpec spec) {
310
    Caffeine<Object, Object> builder = spec.toBuilder();
1✔
311
    builder.strictParsing = false;
1✔
312
    return builder;
1✔
313
  }
314

315
  /**
316
   * Constructs a new {@code Caffeine} instance with the settings specified in {@code spec}.
317
   *
318
   * @param spec a String in the format specified by {@link CaffeineSpec}
319
   * @return a new instance with the specification's settings
320
   */
321
  public static Caffeine<Object, Object> from(String spec) {
322
    return from(CaffeineSpec.parse(spec));
1✔
323
  }
324

325
  /**
326
   * Sets the minimum total size for the internal data structures. Providing a large enough estimate
327
   * at construction time avoids the need for expensive resizing operations later, but setting this
328
   * value unnecessarily high wastes memory.
329
   *
330
   * @param initialCapacity minimum total size for the internal data structures
331
   * @return this {@code Caffeine} instance (for chaining)
332
   * @throws IllegalArgumentException if {@code initialCapacity} is negative
333
   * @throws IllegalStateException if an initial capacity was already set
334
   */
335
  @CanIgnoreReturnValue
336
  public Caffeine<K, V> initialCapacity(int initialCapacity) {
337
    requireState(this.initialCapacity == UNSET_INT,
1!
338
        "initial capacity was already set to %s", this.initialCapacity);
1✔
339
    requireArgument(initialCapacity >= 0);
1✔
340
    this.initialCapacity = initialCapacity;
1✔
341
    return this;
1✔
342
  }
343

344
  boolean hasInitialCapacity() {
345
    return (initialCapacity != UNSET_INT);
1✔
346
  }
347

348
  int getInitialCapacity() {
349
    return hasInitialCapacity() ? initialCapacity : DEFAULT_INITIAL_CAPACITY;
1✔
350
  }
351

352
  /**
353
   * Specifies the executor to use when running asynchronous tasks. The executor is delegated to
354
   * when sending removal notifications, when asynchronous computations are performed by
355
   * {@link AsyncCache} or {@link LoadingCache#refresh} or {@link #refreshAfterWrite}, or when
356
   * performing periodic maintenance. By default, {@link ForkJoinPool#commonPool()} is used.
357
   * <p>
358
   * The primary intent of this method is to facilitate testing of caches which have been configured
359
   * with {@link #removalListener} or utilize asynchronous computations. A test may instead prefer
360
   * to configure the cache to execute tasks directly on the same thread.
361
   * <p>
362
   * Beware that configuring a cache with an executor that discards tasks or never runs them may
363
   * experience non-deterministic behavior.
364
   *
365
   * @param executor the executor to use for asynchronous execution
366
   * @return this {@code Caffeine} instance (for chaining)
367
   * @throws NullPointerException if the specified executor is null
368
   */
369
  @CanIgnoreReturnValue
370
  public Caffeine<K, V> executor(Executor executor) {
371
    requireState(this.executor == null, "executor was already set to %s", this.executor);
1!
372
    this.executor = requireNonNull(executor);
1✔
373
    return this;
1✔
374
  }
375

376
  Executor getExecutor() {
377
    return (executor == null) ? ForkJoinPool.commonPool() : executor;
1✔
378
  }
379

380
  /**
381
   * Specifies the scheduler to use when scheduling routine maintenance based on an expiration
382
   * event. This augments the periodic maintenance that occurs during normal cache operations to
383
   * allow for the prompt removal of expired entries regardless of whether any cache activity is
384
   * occurring at that time. By default, {@link Scheduler#disabledScheduler()} is used.
385
   * <p>
386
   * The scheduling between expiration events is paced to exploit batching and to minimize
387
   * executions in short succession. This minimum difference between the scheduled executions is
388
   * implementation-specific, currently at ~1 second (2^30 ns). In addition, the provided scheduler
389
   * may not offer real-time guarantees (including {@link ScheduledThreadPoolExecutor}). The
390
   * scheduling is best-effort and does not make any hard guarantees of when an expired entry will
391
   * be removed.
392
   *
393
   * @param scheduler the scheduler that submits a task to the {@link #executor(Executor)} after a
394
   *        given delay
395
   * @return this {@code Caffeine} instance (for chaining)
396
   * @throws NullPointerException if the specified scheduler is null
397
   */
398
  @CanIgnoreReturnValue
399
  public Caffeine<K, V> scheduler(Scheduler scheduler) {
400
    requireState(this.scheduler == null, "scheduler was already set to %s", this.scheduler);
1!
401
    this.scheduler = requireNonNull(scheduler);
1✔
402
    return this;
1✔
403
  }
404

405
  Scheduler getScheduler() {
406
    if ((scheduler == null) || (scheduler == Scheduler.disabledScheduler())) {
1!
407
      return Scheduler.disabledScheduler();
1✔
408
    } else if (scheduler == Scheduler.systemScheduler()) {
1✔
409
      return scheduler;
1✔
410
    }
411
    return Scheduler.guardedScheduler(scheduler);
1✔
412
  }
413

414
  /**
415
   * Specifies the maximum number of entries the cache may contain. Note that the cache <b>may evict
416
   * an entry before this limit is exceeded or temporarily exceed the threshold while evicting</b>.
417
   * As the cache size grows close to the maximum, the cache evicts entries that are less likely to
418
   * be used again. For example, the cache may evict an entry because it hasn't been used recently
419
   * or very often.
420
   * <p>
421
   * When {@code maximumSize} is zero, elements will be evicted immediately after being loaded into
422
   * the cache. This can be useful in testing, or to disable caching temporarily without a code
423
   * change. As eviction is scheduled on the configured {@link #executor}, tests may instead prefer
424
   * to configure the cache to execute tasks directly on the same thread.
425
   * <p>
426
   * This feature cannot be used in conjunction with {@link #maximumWeight}.
427
   *
428
   * @param maximumSize the maximum size of the cache
429
   * @return this {@code Caffeine} instance (for chaining)
430
   * @throws IllegalArgumentException if {@code maximumSize} is negative
431
   * @throws IllegalStateException if a maximum size or weight was already set
432
   */
433
  @CanIgnoreReturnValue
434
  public Caffeine<K, V> maximumSize(long maximumSize) {
435
    requireState(this.maximumSize == UNSET_INT,
1✔
436
        "maximum size was already set to %s", this.maximumSize);
1✔
437
    requireState(this.maximumWeight == UNSET_INT,
1!
438
        "maximum weight was already set to %s", this.maximumWeight);
1✔
439
    requireState(this.weigher == null, "maximum size cannot be combined with weigher");
1✔
440
    requireArgument(maximumSize >= 0, "maximum size must not be negative");
1✔
441
    this.maximumSize = maximumSize;
1✔
442
    return this;
1✔
443
  }
444

445
  /**
446
   * Specifies the maximum weight of entries the cache may contain. Weight is determined using the
447
   * {@link Weigher} specified with {@link #weigher}, and use of this method requires a
448
   * corresponding call to {@link #weigher} prior to calling {@link #build}.
449
   * <p>
450
   * Note that the cache <b>may evict an entry before this limit is exceeded or temporarily exceed
451
   * the threshold while evicting</b>. As the cache size grows close to the maximum, the cache
452
   * evicts entries that are less likely to be used again. For example, the cache may evict an entry
453
   * because it hasn't been used recently or very often.
454
   * <p>
455
   * When {@code maximumWeight} is zero, elements will be evicted immediately after being loaded
456
   * into the cache. This can be useful in testing, or to disable caching temporarily without a code
457
   * change. As eviction is scheduled on the configured {@link #executor}, tests may instead prefer
458
   * to configure the cache to execute tasks directly on the same thread.
459
   * <p>
460
   * Note that weight is only used to determine whether the cache is over capacity; it has no effect
461
   * on selecting which entry should be evicted next.
462
   * <p>
463
   * This feature cannot be used in conjunction with {@link #maximumSize}.
464
   *
465
   * @param maximumWeight the maximum total weight of entries the cache may contain
466
   * @return this {@code Caffeine} instance (for chaining)
467
   * @throws IllegalArgumentException if {@code maximumWeight} is negative
468
   * @throws IllegalStateException if a maximum weight or size was already set
469
   */
470
  @CanIgnoreReturnValue
471
  public Caffeine<K, V> maximumWeight(long maximumWeight) {
472
    requireState(this.maximumWeight == UNSET_INT,
1✔
473
        "maximum weight was already set to %s", this.maximumWeight);
1✔
474
    requireState(this.maximumSize == UNSET_INT,
1!
475
        "maximum size was already set to %s", this.maximumSize);
1✔
476
    requireArgument(maximumWeight >= 0, "maximum weight must not be negative");
1✔
477
    this.maximumWeight = maximumWeight;
1✔
478
    return this;
1✔
479
  }
480

481
  /**
482
   * Specifies the weigher to use in determining the weight of entries. Entry weight is taken into
483
   * consideration by {@link #maximumWeight(long)} when determining which entries to evict, and use
484
   * of this method requires a corresponding call to {@link #maximumWeight(long)} prior to calling
485
   * {@link #build}. Weights are measured and recorded when entries are inserted into or updated in
486
   * the cache, and are thus effectively static during the lifetime of a cache entry.
487
   * <p>
488
   * When the weight of an entry is zero it will not be considered for size-based eviction (though
489
   * it still may be evicted by other means).
490
   * <p>
491
   * <b>Important note:</b> Instead of returning <em>this</em> as a {@code Caffeine} instance, this
492
   * method returns {@code Caffeine<K1, V1>}. From this point on, either the original reference or
493
   * the returned reference may be used to complete configuration and build the cache, but only the
494
   * "generic" one is type-safe. That is, it will properly prevent you from building caches whose
495
   * key or value types are incompatible with the types accepted by the weigher already provided;
496
   * the {@code Caffeine} type cannot do this. For best results, simply use the standard
497
   * method-chaining idiom, as illustrated in the documentation at top, configuring a
498
   * {@code Caffeine} and building your {@link Cache} all in a single statement.
499
   * <p>
500
   * <b>Warning:</b> if you ignore the above advice, and use this {@code Caffeine} to build a cache
501
   * whose key or value type is incompatible with the weigher, you will likely experience a
502
   * {@link ClassCastException} at some <i>undefined</i> point in the future.
503
   *
504
   * @param weigher the weigher to use in calculating the weight of cache entries
505
   * @param <K1> key type of the weigher
506
   * @param <V1> value type of the weigher
507
   * @return the cache builder reference that should be used instead of {@code this} for any
508
   *         remaining configuration and cache building
509
   * @throws IllegalStateException if a weigher was already set
510
   */
511
  @CanIgnoreReturnValue
512
  @SuppressWarnings("PMD.TypeParameterNamingConventions")
513
  public <K1 extends K, V1 extends V> Caffeine<K1, V1> weigher(
514
      Weigher<? super K1, ? super V1> weigher) {
515
    requireNonNull(weigher);
1✔
516
    requireState(this.weigher == null, "weigher was already set to %s", this.weigher);
1!
517
    requireState(!strictParsing || this.maximumSize == UNSET_INT,
1✔
518
        "weigher cannot be combined with maximum size");
519

520
    @SuppressWarnings("unchecked")
521
    var self = (Caffeine<K1, V1>) this;
1✔
522
    self.weigher = weigher;
1✔
523
    return self;
1✔
524
  }
525

526
  boolean evicts() {
527
    return getMaximum() != UNSET_INT;
1✔
528
  }
529

530
  boolean isWeighted() {
531
    return (weigher != null);
1✔
532
  }
533

534
  long getMaximum() {
535
    return isWeighted() ? maximumWeight : maximumSize;
1✔
536
  }
537

538
  @SuppressWarnings({"JavaAnnotator", "PMD.TypeParameterNamingConventions", "unchecked"})
539
  <K1 extends K, V1 extends V> Weigher<K1, V1> getWeigher(boolean isAsync) {
540
    Weigher<K1, V1> delegate = (weigher == null) || (weigher == Weigher.singletonWeigher())
1✔
541
        ? Weigher.singletonWeigher()
1✔
542
        : Weigher.boundedWeigher((Weigher<K1, V1>) weigher);
1✔
543
    return isAsync ? (Weigher<K1, V1>) new AsyncWeigher<>(delegate) : delegate;
1✔
544
  }
545

546
  /**
547
   * Specifies that each key (not value) stored in the cache should be wrapped in a
548
   * {@link WeakReference} (by default, strong references are used).
549
   * <p>
550
   * <b>Warning:</b> when this method is used, the resulting cache will use identity ({@code ==})
551
   * comparison to determine equality of keys. Its {@link Cache#asMap} view will therefore
552
   * technically violate the {@link Map} specification (in the same way that {@link IdentityHashMap}
553
   * does).
554
   * <p>
555
   * Entries with keys that have been garbage collected may be counted in
556
   * {@link Cache#estimatedSize()}, but will never be visible to read or write operations; such
557
   * entries are cleaned up as part of the routine maintenance described in the class Javadoc.
558
   * <p>
559
   * This feature cannot be used in conjunction when {@link #evictionListener(RemovalListener)} is
560
   * combined with {@link #buildAsync}.
561
   *
562
   * @return this {@code Caffeine} instance (for chaining)
563
   * @throws IllegalStateException if the key strength was already set
564
   */
565
  @CanIgnoreReturnValue
566
  public Caffeine<K, V> weakKeys() {
567
    requireState(keyStrength == null, "Key strength was already set to %s", keyStrength);
1✔
568
    keyStrength = Strength.WEAK;
1✔
569
    return this;
1✔
570
  }
571

572
  boolean isStrongKeys() {
573
    return (keyStrength == null);
1✔
574
  }
575

576
  /**
577
   * Specifies that each value (not key) stored in the cache should be wrapped in a
578
   * {@link WeakReference} (by default, strong references are used).
579
   * <p>
580
   * Weak values will be garbage collected once they are weakly reachable. This makes them a poor
581
   * candidate for caching; consider {@link #softValues} instead.
582
   * <p>
583
   * <b>Note:</b> when this method is used, the resulting cache will use identity ({@code ==})
584
   * comparison to determine equality of values.
585
   * <p>
586
   * Entries with values that have been garbage collected may be counted in
587
   * {@link Cache#estimatedSize()}, but will never be visible to read or write operations; such
588
   * entries are cleaned up as part of the routine maintenance described in the class Javadoc.
589
   * <p>
590
   * This feature cannot be used in conjunction with {@link #buildAsync}.
591
   *
592
   * @return this {@code Caffeine} instance (for chaining)
593
   * @throws IllegalStateException if the value strength was already set
594
   */
595
  @CanIgnoreReturnValue
596
  public Caffeine<K, V> weakValues() {
597
    requireState(valueStrength == null, "Value strength was already set to %s", valueStrength);
1!
598
    valueStrength = Strength.WEAK;
1✔
599
    return this;
1✔
600
  }
601

602
  boolean isStrongValues() {
603
    return (valueStrength == null);
1✔
604
  }
605

606
  boolean isWeakValues() {
607
    return (valueStrength == Strength.WEAK);
1✔
608
  }
609

610
  /**
611
   * Specifies that each value (not key) stored in the cache should be wrapped in a
612
   * {@link SoftReference} (by default, strong references are used). Softly-referenced objects will
613
   * be garbage-collected in a <i>globally</i> least-recently-used manner, in response to memory
614
   * demand.
615
   * <p>
616
   * <b>Warning:</b> in most circumstances it is better to set a per-cache
617
   * {@linkplain #maximumSize(long) maximum size} instead of using soft references. You should only
618
   * use this method if you are very familiar with the practical consequences of soft references.
619
   * <p>
620
   * <b>Note:</b> when this method is used, the resulting cache will use identity ({@code ==})
621
   * comparison to determine equality of values.
622
   * <p>
623
   * Entries with values that have been garbage collected may be counted in
624
   * {@link Cache#estimatedSize()}, but will never be visible to read or write operations; such
625
   * entries are cleaned up as part of the routine maintenance described in the class Javadoc.
626
   * <p>
627
   * This feature cannot be used in conjunction with {@link #buildAsync}.
628
   *
629
   * @return this {@code Caffeine} instance (for chaining)
630
   * @throws IllegalStateException if the value strength was already set
631
   */
632
  @CanIgnoreReturnValue
633
  public Caffeine<K, V> softValues() {
634
    requireState(valueStrength == null, "Value strength was already set to %s", valueStrength);
1✔
635
    valueStrength = Strength.SOFT;
1✔
636
    return this;
1✔
637
  }
638

639
  /**
640
   * Specifies that each entry should be automatically removed from the cache once a fixed duration
641
   * has elapsed after the entry's creation, or the most recent replacement of its value.
642
   * <p>
643
   * Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible to
644
   * read or write operations. Expired entries are cleaned up as part of the routine maintenance
645
   * described in the class Javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt
646
   * removal of expired entries.
647
   *
648
   * @param duration the length of time after an entry is created or updated before it should be
649
   *        automatically removed
650
   * @return this {@code Caffeine} instance (for chaining)
651
   * @throws IllegalArgumentException if {@code duration} is negative
652
   * @throws IllegalStateException if {@link #expireAfterWrite} or
653
   *        {@link #expireAfter(Expiry)} was already set
654
   */
655
  @CanIgnoreReturnValue
656
  public Caffeine<K, V> expireAfterWrite(Duration duration) {
657
    return expireAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS);
1✔
658
  }
659

660
  /**
661
   * Specifies that each entry should be automatically removed from the cache once a fixed duration
662
   * has elapsed after the entry's creation, or the most recent replacement of its value.
663
   * <p>
664
   * Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible to
665
   * read or write operations. Expired entries are cleaned up as part of the routine maintenance
666
   * described in the class Javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt
667
   * removal of expired entries.
668
   * <p>
669
   * If you can represent the duration as a {@link java.time.Duration} (which should be preferred
670
   * when feasible), use {@link #expireAfterWrite(Duration)} instead.
671
   *
672
   * @param duration the length of time after an entry is created or updated before it should be
673
   *        automatically removed
674
   * @param unit the unit that {@code duration} is expressed in
675
   * @return this {@code Caffeine} instance (for chaining)
676
   * @throws IllegalArgumentException if {@code duration} is negative
677
   * @throws IllegalStateException if {@link #expireAfterWrite} or
678
   *        {@link #expireAfter(Expiry)} was already set
679
   */
680
  @CanIgnoreReturnValue
681
  public Caffeine<K, V> expireAfterWrite(long duration, TimeUnit unit) {
682
    requireState(expireAfterWriteNanos == UNSET_INT,
1✔
683
        "expireAfterWrite was already set to %s ns", expireAfterWriteNanos);
1✔
684
    requireState(expiry == null, "expireAfterWrite may not be used with variable expiration");
1!
685
    requireArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit);
1✔
686
    this.expireAfterWriteNanos = unit.toNanos(duration);
1✔
687
    return this;
1✔
688
  }
689

690
  long getExpiresAfterWriteNanos() {
691
    return expireAfterWriteNanos;
1✔
692
  }
693

694
  boolean expiresAfterWrite() {
695
    return (expireAfterWriteNanos != UNSET_INT);
1✔
696
  }
697

698
  /**
699
   * Specifies that each entry should be automatically removed from the cache once a fixed duration
700
   * has elapsed after the entry's creation, the most recent replacement of its value, or its last
701
   * access. Access time is reset by all cache read and write operations (including {@code
702
   * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by operations on the
703
   * collection-views of {@link Cache#asMap}.
704
   * <p>
705
   * Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible to
706
   * read or write operations. Expired entries are cleaned up as part of the routine maintenance
707
   * described in the class Javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt
708
   * removal of expired entries.
709
   *
710
   * @param duration the length of time after an entry is last accessed before it should be
711
   *        automatically removed
712
   * @return this {@code Caffeine} instance (for chaining)
713
   * @throws IllegalArgumentException if {@code duration} is negative
714
   * @throws IllegalStateException if {@link #expireAfterAccess} or
715
   *        {@link #expireAfter(Expiry)} was already set
716
   */
717
  @CanIgnoreReturnValue
718
  public Caffeine<K, V> expireAfterAccess(Duration duration) {
719
    return expireAfterAccess(toNanosSaturated(duration), TimeUnit.NANOSECONDS);
1✔
720
  }
721

722
  /**
723
   * Specifies that each entry should be automatically removed from the cache once a fixed duration
724
   * has elapsed after the entry's creation, the most recent replacement of its value, or its last
725
   * read. Access time is reset by all cache read and write operations (including
726
   * {@code Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by operations
727
   * on the collection-views of {@link Cache#asMap}.
728
   * <p>
729
   * Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible to
730
   * read or write operations. Expired entries are cleaned up as part of the routine maintenance
731
   * described in the class Javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt
732
   * removal of expired entries.
733
   * <p>
734
   * If you can represent the duration as a {@link java.time.Duration} (which should be preferred
735
   * when feasible), use {@link #expireAfterAccess(Duration)} instead.
736
   *
737
   * @param duration the length of time after an entry is last accessed before it should be
738
   *        automatically removed
739
   * @param unit the unit that {@code duration} is expressed in
740
   * @return this {@code Caffeine} instance (for chaining)
741
   * @throws IllegalArgumentException if {@code duration} is negative
742
   * @throws IllegalStateException if {@link #expireAfterAccess} or
743
   *        {@link #expireAfter(Expiry)} was already set
744
   */
745
  @CanIgnoreReturnValue
746
  public Caffeine<K, V> expireAfterAccess(long duration, TimeUnit unit) {
747
    requireState(expireAfterAccessNanos == UNSET_INT,
1✔
748
        "expireAfterAccess was already set to %s ns", expireAfterAccessNanos);
1✔
749
    requireState(expiry == null, "expireAfterAccess may not be used with variable expiration");
1!
750
    requireArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit);
1!
751
    this.expireAfterAccessNanos = unit.toNanos(duration);
1✔
752
    return this;
1✔
753
  }
754

755
  long getExpiresAfterAccessNanos() {
756
    return expireAfterAccessNanos;
1✔
757
  }
758

759
  boolean expiresAfterAccess() {
760
    return (expireAfterAccessNanos != UNSET_INT);
1✔
761
  }
762

763
  /**
764
   * Specifies that each entry should be automatically removed from the cache once a duration has
765
   * elapsed after the entry's creation, the most recent replacement of its value, or its last
766
   * read. The expiration time is reevaluated by all cache read and write operations (including
767
   * {@code Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by operations
768
   * on the collection-views of {@link Cache#asMap}.
769
   * <p>
770
   * Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible to
771
   * read or write operations. Expired entries are cleaned up as part of the routine maintenance
772
   * described in the class Javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt
773
   * removal of expired entries.
774
   * <p>
775
   * <b>Important note:</b> after invoking this method, do not continue to use <i>this</i> cache
776
   * builder reference; instead use the reference this method <i>returns</i>. At runtime, these
777
   * point to the same instance, but only the returned reference has the correct generic type
778
   * information so as to ensure type safety. For best results, use the standard method-chaining
779
   * idiom illustrated in the class documentation above, configuring a builder and building your
780
   * cache in a single statement. Failure to heed this advice can result in a
781
   * {@link ClassCastException} being thrown by a cache operation at some <i>undefined</i> point in
782
   * the future.
783
   *
784
   * @param expiry the expiry to use in calculating the expiration time of cache entries
785
   * @param <K1> key type of the expiry
786
   * @param <V1> value type of the expiry
787
   * @return this {@code Caffeine} instance (for chaining)
788
   * @throws IllegalStateException if expiration was already set
789
   */
790
  @CanIgnoreReturnValue
791
  @SuppressWarnings("PMD.TypeParameterNamingConventions")
792
  public <K1 extends K, V1 extends V> Caffeine<K1, V1> expireAfter(
793
      Expiry<? super K1, ? super V1> expiry) {
794
    requireNonNull(expiry);
1✔
795
    requireState(this.expiry == null, "Expiry was already set to %s", this.expiry);
1✔
796
    requireState(this.expireAfterAccessNanos == UNSET_INT,
1!
797
        "Expiry may not be used with expiresAfterAccess");
798
    requireState(this.expireAfterWriteNanos == UNSET_INT,
1✔
799
        "Expiry may not be used with expiresAfterWrite");
800

801
    @SuppressWarnings("unchecked")
802
    var self = (Caffeine<K1, V1>) this;
1✔
803
    self.expiry = expiry;
1✔
804
    return self;
1✔
805
  }
806

807
  boolean expiresVariable() {
808
    return expiry != null;
1✔
809
  }
810

811
  @SuppressWarnings("unchecked")
812
  @Nullable Expiry<K, V> getExpiry(boolean isAsync) {
813
    return isAsync && (expiry != null)
1✔
814
        ? (Expiry<K, V>) new AsyncExpiry<>(expiry)
1✔
815
        : (Expiry<K, V>) expiry;
1✔
816
  }
817

818
  /**
819
   * Specifies that active entries are eligible for automatic refresh once a fixed duration has
820
   * elapsed after the entry's creation, or the most recent replacement of its value. The semantics
821
   * of refreshes are specified in {@link LoadingCache#refresh}, and are performed by calling
822
   * {@link AsyncCacheLoader#asyncReload}.
823
   * <p>
824
   * Automatic refreshes are performed when the first stale request for an entry occurs. The request
825
   * triggering the refresh will make a synchronous call to {@link AsyncCacheLoader#asyncReload} to
826
   * obtain a future of the new value. If the returned future is already complete, it is returned
827
   * immediately. Otherwise, the old value is returned.
828
   * <p>
829
   * <b>Note:</b> <i>all exceptions thrown during refresh will be logged and then swallowed</i>.
830
   *
831
   * @param duration the length of time after an entry is created that it should be considered
832
   *     stale, and thus eligible for refresh
833
   * @return this {@code Caffeine} instance (for chaining)
834
   * @throws IllegalArgumentException if {@code duration} is zero or negative
835
   * @throws IllegalStateException if the refresh interval was already set
836
   */
837
  @CanIgnoreReturnValue
838
  public Caffeine<K, V> refreshAfterWrite(Duration duration) {
839
    return refreshAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS);
1✔
840
  }
841

842
  /**
843
   * Specifies that active entries are eligible for automatic refresh once a fixed duration has
844
   * elapsed after the entry's creation, or the most recent replacement of its value. The semantics
845
   * of refreshes are specified in {@link LoadingCache#refresh}, and are performed by calling
846
   * {@link AsyncCacheLoader#asyncReload}.
847
   * <p>
848
   * Automatic refreshes are performed when the first stale request for an entry occurs. The request
849
   * triggering the refresh will make a synchronous call to {@link AsyncCacheLoader#asyncReload} to
850
   * obtain a future of the new value. If the returned future is already complete, it is returned
851
   * immediately. Otherwise, the old value is returned.
852
   * <p>
853
   * <b>Note:</b> <i>all exceptions thrown during refresh will be logged and then swallowed</i>.
854
   * <p>
855
   * If you can represent the duration as a {@link java.time.Duration} (which should be preferred
856
   * when feasible), use {@link #refreshAfterWrite(Duration)} instead.
857
   *
858
   * @param duration the length of time after an entry is created that it should be considered
859
   *        stale, and thus eligible for refresh
860
   * @param unit the unit that {@code duration} is expressed in
861
   * @return this {@code Caffeine} instance (for chaining)
862
   * @throws IllegalArgumentException if {@code duration} is zero or negative
863
   * @throws IllegalStateException if the refresh interval was already set
864
   */
865
  @CanIgnoreReturnValue
866
  public Caffeine<K, V> refreshAfterWrite(long duration, TimeUnit unit) {
867
    requireNonNull(unit);
1✔
868
    requireState(refreshAfterWriteNanos == UNSET_INT,
1✔
869
        "refreshAfterWriteNanos was already set to %s ns", refreshAfterWriteNanos);
1✔
870
    requireArgument(duration > 0, "duration must be positive: %s %s", duration, unit);
1✔
871
    this.refreshAfterWriteNanos = unit.toNanos(duration);
1✔
872
    return this;
1✔
873
  }
874

875
  long getRefreshAfterWriteNanos() {
876
    return refreshAfterWriteNanos;
1✔
877
  }
878

879
  boolean refreshAfterWrite() {
880
    return refreshAfterWriteNanos != UNSET_INT;
1✔
881
  }
882

883
  /**
884
   * Specifies a nanosecond-precision time source for use in determining when entries should be
885
   * expired or refreshed. By default, {@link System#nanoTime} is used.
886
   * <p>
887
   * The primary intent of this method is to facilitate testing of caches which have been configured
888
   * with {@link #expireAfterWrite}, {@link #expireAfterAccess}, or {@link #refreshAfterWrite}. Note
889
   * that this ticker is not used when recording statistics.
890
   *
891
   * @param ticker a nanosecond-precision time source
892
   * @return this {@code Caffeine} instance (for chaining)
893
   * @throws IllegalStateException if a ticker was already set
894
   * @throws NullPointerException if the specified ticker is null
895
   */
896
  @CanIgnoreReturnValue
897
  public Caffeine<K, V> ticker(Ticker ticker) {
898
    requireState(this.ticker == null, "Ticker was already set to %s", this.ticker);
1✔
899
    this.ticker = requireNonNull(ticker);
1✔
900
    return this;
1✔
901
  }
902

903
  Ticker getTicker() {
904
    boolean useTicker = expiresVariable() || expiresAfterAccess()
1✔
905
        || expiresAfterWrite() || refreshAfterWrite();
1✔
906
    return useTicker
1✔
907
        ? (ticker == null) ? Ticker.systemTicker() : ticker
1✔
908
        : Ticker.disabledTicker();
1✔
909
  }
910

911
  /**
912
   * Specifies a listener instance that caches should notify each time an entry is evicted. The
913
   * cache will invoke this listener during the atomic operation to remove the entry. In the case of
914
   * expiration or reference collection, the entry may be pending removal and will be discarded as
915
   * part of the routine maintenance described in the class documentation above. For a more prompt
916
   * notification on expiration a {@link #scheduler(Scheduler)} may be configured. A
917
   * {@link #removalListener(RemovalListener)} may be preferred when the listener should be invoked
918
   * for any {@linkplain RemovalCause reason}, be performed outside of the atomic operation to
919
   * remove the entry, or be delegated to the configured {@link #executor(Executor)}.
920
   * <p>
921
   * <b>Important note:</b> after invoking this method, do not continue to use <i>this</i> cache
922
   * builder reference; instead use the reference this method <i>returns</i>. At runtime, these
923
   * point to the same instance, but only the returned reference has the correct generic type
924
   * information so as to ensure type safety. For best results, use the standard method-chaining
925
   * idiom illustrated in the class documentation above, configuring a builder and building your
926
   * cache in a single statement. Failure to heed this advice can result in a
927
   * {@link ClassCastException} being thrown by a cache operation at some <i>undefined</i> point in
928
   * the future.
929
   * <p>
930
   * <b>Warning:</b> The {@code evictionListener} <b>must not</b> modify this cache and any
931
   * exception thrown will <i>not</i> be propagated to the {@code Cache} user, only logged via a
932
   * {@link Logger}.
933
   * <p>
934
   * This feature cannot be used in conjunction when {@link #weakKeys()} is combined with
935
   * {@link #buildAsync}.
936
   *
937
   * @param evictionListener a listener instance that caches should notify each time an entry is
938
   *        being automatically removed due to eviction
939
   * @param <K1> the key type of the listener
940
   * @param <V1> the value type of the listener
941
   * @return the cache builder reference that should be used instead of {@code this} for any
942
   *         remaining configuration and cache building
943
   * @throws IllegalStateException if a removal listener was already set
944
   * @throws NullPointerException if the specified removal listener is null
945
   */
946
  @CanIgnoreReturnValue
947
  @SuppressWarnings("PMD.TypeParameterNamingConventions")
948
  public <K1 extends K, V1 extends V> Caffeine<K1, V1> evictionListener(
949
      RemovalListener<? super K1, ? super V1> evictionListener) {
950
    requireState(this.evictionListener == null,
1✔
951
        "eviction listener was already set to %s", this.evictionListener);
952

953
    @SuppressWarnings("unchecked")
954
    var self = (Caffeine<K1, V1>) this;
1✔
955
    self.evictionListener = requireNonNull(evictionListener);
1✔
956
    return self;
1✔
957
  }
958

959
  @SuppressWarnings({"JavaAnnotator", "PMD.TypeParameterNamingConventions", "unchecked"})
960
  <K1 extends K, V1 extends V> @Nullable RemovalListener<K1, V1> getEvictionListener(
961
      boolean async) {
962
    var castedListener = (RemovalListener<K1, V1>) evictionListener;
1✔
963
    return async && (castedListener != null)
1✔
964
        ? (RemovalListener<K1, V1>) new AsyncEvictionListener<>(castedListener)
1✔
965
        : castedListener;
1✔
966
  }
967

968
  /**
969
   * Specifies a listener instance that caches should notify each time an entry is removed for any
970
   * {@linkplain RemovalCause reason}. The cache will invoke this listener on the configured
971
   * {@link #executor(Executor)} after the entry's removal operation has completed. In the case of
972
   * expiration or reference collection, the entry may be pending removal and will be discarded as
973
   * part of the routine maintenance described in the class documentation above. For a more prompt
974
   * notification on expiration a {@link #scheduler(Scheduler)} may be configured. An
975
   * {@link #evictionListener(RemovalListener)} may be preferred when the listener should be invoked
976
   * as part of the atomic operation to remove the entry.
977
   * <p>
978
   * <b>Important note:</b> after invoking this method, do not continue to use <i>this</i> cache
979
   * builder reference; instead use the reference this method <i>returns</i>. At runtime, these
980
   * point to the same instance, but only the returned reference has the correct generic type
981
   * information so as to ensure type safety. For best results, use the standard method-chaining
982
   * idiom illustrated in the class documentation above, configuring a builder and building your
983
   * cache in a single statement. Failure to heed this advice can result in a
984
   * {@link ClassCastException} being thrown by a cache operation at some <i>undefined</i> point in
985
   * the future.
986
   * <p>
987
   * <b>Warning:</b> any exception thrown by {@code removalListener} will <i>not</i> be propagated
988
   * to the {@code Cache} user, only logged via a {@link Logger}.
989
   *
990
   * @param removalListener a listener instance that caches should notify each time an entry is
991
   *        removed
992
   * @param <K1> the key type of the listener
993
   * @param <V1> the value type of the listener
994
   * @return the cache builder reference that should be used instead of {@code this} for any
995
   *         remaining configuration and cache building
996
   * @throws IllegalStateException if a removal listener was already set
997
   * @throws NullPointerException if the specified removal listener is null
998
   */
999
  @CanIgnoreReturnValue
1000
  @SuppressWarnings("PMD.TypeParameterNamingConventions")
1001
  public <K1 extends K, V1 extends V> Caffeine<K1, V1> removalListener(
1002
      RemovalListener<? super K1, ? super V1> removalListener) {
1003
    requireState(this.removalListener == null,
1!
1004
        "removal listener was already set to %s", this.removalListener);
1005

1006
    @SuppressWarnings("unchecked")
1007
    var self = (Caffeine<K1, V1>) this;
1✔
1008
    self.removalListener = requireNonNull(removalListener);
1✔
1009
    return self;
1✔
1010
  }
1011

1012
  @SuppressWarnings({"JavaAnnotator", "PMD.TypeParameterNamingConventions", "unchecked"})
1013
  <K1 extends K, V1 extends V> @Nullable RemovalListener<K1, V1> getRemovalListener(boolean async) {
1014
    var castedListener = (RemovalListener<K1, V1>) removalListener;
1✔
1015
    return async && (castedListener != null)
1✔
1016
        ? (RemovalListener<K1, V1>) new AsyncRemovalListener<>(castedListener, getExecutor())
1✔
1017
        : castedListener;
1✔
1018
  }
1019

1020
  /**
1021
   * Enables the accumulation of {@link CacheStats} during the operation of the cache. Without this
1022
   * {@link Cache#stats} will return zero for all statistics. Note that recording statistics
1023
   * requires bookkeeping to be performed with each operation, and thus imposes a performance
1024
   * penalty on cache operations.
1025
   *
1026
   * @return this {@code Caffeine} instance (for chaining)
1027
   */
1028
  @CanIgnoreReturnValue
1029
  public Caffeine<K, V> recordStats() {
1030
    requireState(this.statsCounterSupplier == null, "Statistics recording was already set");
1✔
1031
    statsCounterSupplier = ENABLED_STATS_COUNTER_SUPPLIER;
1✔
1032
    return this;
1✔
1033
  }
1034

1035
  /**
1036
   * Enables the accumulation of {@link CacheStats} during the operation of the cache. Without this
1037
   * {@link Cache#stats} will return zero for all statistics. Note that recording statistics
1038
   * requires bookkeeping to be performed with each operation, and thus imposes a performance
1039
   * penalty on cache operations. Any exception thrown by the supplied {@link StatsCounter} will be
1040
   * suppressed and logged.
1041
   *
1042
   * @param statsCounterSupplier a supplier instance that returns a new {@link StatsCounter}
1043
   * @return this {@code Caffeine} instance (for chaining)
1044
   */
1045
  @CanIgnoreReturnValue
1046
  public Caffeine<K, V> recordStats(Supplier<? extends StatsCounter> statsCounterSupplier) {
1047
    requireState(this.statsCounterSupplier == null, "Statistics recording was already set");
1✔
1048
    requireNonNull(statsCounterSupplier);
1✔
1049
    this.statsCounterSupplier = () -> StatsCounter.guardedStatsCounter(statsCounterSupplier.get());
1✔
1050
    return this;
1✔
1051
  }
1052

1053
  boolean isRecordingStats() {
1054
    return (statsCounterSupplier != null);
1✔
1055
  }
1056

1057
  Supplier<StatsCounter> getStatsCounterSupplier() {
1058
    return (statsCounterSupplier == null)
1✔
1059
        ? StatsCounter::disabledStatsCounter
1✔
1060
        : statsCounterSupplier;
1✔
1061
  }
1062

1063
  boolean isBounded() {
1064
    return (maximumSize != UNSET_INT)
1✔
1065
        || (maximumWeight != UNSET_INT)
1066
        || (expireAfterAccessNanos != UNSET_INT)
1067
        || (expireAfterWriteNanos != UNSET_INT)
1068
        || (expiry != null)
1069
        || (keyStrength != null)
1070
        || (valueStrength != null);
1071
  }
1072

1073
  /**
1074
   * Builds a cache which does not automatically load values when keys are requested unless a
1075
   * mapping function is provided. Note that multiple threads can concurrently load values for
1076
   * distinct keys.
1077
   * <p>
1078
   * Consider {@link #build(CacheLoader)} instead, if it is feasible to implement a
1079
   * {@code CacheLoader}.
1080
   * <p>
1081
   * This method does not alter the state of this {@code Caffeine} instance, so it can be invoked
1082
   * again to create multiple independent caches.
1083
   *
1084
   * @param <K1> the key type of the cache
1085
   * @param <V1> the value type of the cache
1086
   * @return a cache having the requested features
1087
   */
1088
  @SuppressWarnings("PMD.TypeParameterNamingConventions")
1089
  public <K1 extends K, V1 extends @Nullable V> Cache<K1, V1> build() {
1090
    requireWeightWithWeigher();
1✔
1091
    requireNonLoadingCache();
1✔
1092

1093
    @SuppressWarnings("unchecked")
1094
    var self = (Caffeine<K1, V1>) this;
1✔
1095
    return isBounded()
1✔
1096
        ? new BoundedLocalCache.BoundedLocalManualCache<>(self)
1✔
1097
        : new UnboundedLocalCache.UnboundedLocalManualCache<>(self);
1✔
1098
  }
1099

1100
  /**
1101
   * Builds a cache, which either returns an already-loaded value for a given key or atomically
1102
   * computes or retrieves it using the supplied {@code CacheLoader}. If another thread is currently
1103
   * loading the value for this key, simply waits for that thread to finish and returns its loaded
1104
   * value. Note that multiple threads can concurrently load values for distinct keys.
1105
   * <p>
1106
   * This method does not alter the state of this {@code Caffeine} instance, so it can be invoked
1107
   * again to create multiple independent caches.
1108
   *
1109
   * @param loader the cache loader used to obtain new values
1110
   * @param <K1> the key type of the loader
1111
   * @param <V1> the value type of the loader
1112
   * @return a cache having the requested features
1113
   */
1114
  @SuppressWarnings("PMD.TypeParameterNamingConventions")
1115
  public <K1 extends K, V1 extends @Nullable V> LoadingCache<K1, V1> build(
1116
      CacheLoader<? super K1, V1> loader) {
1117
    requireWeightWithWeigher();
1✔
1118

1119
    @SuppressWarnings("unchecked")
1120
    var self = (Caffeine<K1, V1>) this;
1✔
1121
    return isBounded() || refreshAfterWrite()
1✔
1122
        ? new BoundedLocalCache.BoundedLocalLoadingCache<>(self, loader)
1✔
1123
        : new UnboundedLocalCache.UnboundedLocalLoadingCache<>(self, loader);
1✔
1124
  }
1125

1126
  /**
1127
   * Builds a cache which does not automatically load values when keys are requested unless a
1128
   * mapping function is provided. The returned {@link CompletableFuture} may be already loaded or
1129
   * currently computing the value for a given key. If the asynchronous computation fails or
1130
   * computes a {@code null} value then the entry will be automatically removed. Note that multiple
1131
   * threads can concurrently load values for distinct keys.
1132
   * <p>
1133
   * Consider {@link #buildAsync(CacheLoader)} or {@link #buildAsync(AsyncCacheLoader)} instead, if
1134
   * it is feasible to implement an {@code CacheLoader} or {@code AsyncCacheLoader}.
1135
   * <p>
1136
   * This method does not alter the state of this {@code Caffeine} instance, so it can be invoked
1137
   * again to create multiple independent caches.
1138
   * <p>
1139
   * This construction cannot be used with {@link #weakValues()}, {@link #softValues()}, or when
1140
   * {@link #weakKeys()} are combined with {@link #evictionListener(RemovalListener)}.
1141
   *
1142
   * @param <K1> the key type of the cache
1143
   * @param <V1> the value type of the cache
1144
   * @return a cache having the requested features
1145
   */
1146
  @SuppressWarnings("PMD.TypeParameterNamingConventions")
1147
  public <K1 extends K, V1 extends @Nullable V> AsyncCache<K1, V1> buildAsync() {
1148
    requireState(valueStrength == null, "Weak or soft values cannot be combined with AsyncCache");
1✔
1149
    requireState(isStrongKeys() || (evictionListener == null),
1!
1150
        "Weak keys cannot be combined with eviction listener and AsyncLoadingCache");
1151
    requireWeightWithWeigher();
1✔
1152
    requireNonLoadingCache();
1✔
1153

1154
    @SuppressWarnings("unchecked")
1155
    var self = (Caffeine<K1, V1>) this;
1✔
1156
    return isBounded()
1✔
1157
        ? new BoundedLocalCache.BoundedLocalAsyncCache<>(self)
1✔
1158
        : new UnboundedLocalCache.UnboundedLocalAsyncCache<>(self);
1✔
1159
  }
1160

1161
  /**
1162
   * Builds a cache, which either returns a {@link CompletableFuture} already loaded or currently
1163
   * computing the value for a given key, or atomically computes the value asynchronously through a
1164
   * supplied mapping function or the supplied {@code CacheLoader}. If the asynchronous computation
1165
   * fails or computes a {@code null} value then the entry will be automatically removed. Note that
1166
   * multiple threads can concurrently load values for distinct keys.
1167
   * <p>
1168
   * This method does not alter the state of this {@code Caffeine} instance, so it can be invoked
1169
   * again to create multiple independent caches.
1170
   * <p>
1171
   * This construction cannot be used with {@link #weakValues()}, {@link #softValues()}, or when
1172
   * {@link #weakKeys()} are combined with {@link #evictionListener(RemovalListener)}.
1173
   *
1174
   * @param loader the cache loader used to obtain new values
1175
   * @param <K1> the key type of the loader
1176
   * @param <V1> the value type of the loader
1177
   * @return a cache having the requested features
1178
   */
1179
  @SuppressWarnings("PMD.TypeParameterNamingConventions")
1180
  public <K1 extends K, V1 extends @Nullable V> AsyncLoadingCache<K1, V1> buildAsync(
1181
      CacheLoader<? super K1, V1> loader) {
1182
    return buildAsync((AsyncCacheLoader<? super K1, V1>) loader);
1✔
1183
  }
1184

1185
  /**
1186
   * Builds a cache, which either returns a {@link CompletableFuture} already loaded or currently
1187
   * computing the value for a given key, or atomically computes the value asynchronously through a
1188
   * supplied mapping function or the supplied {@code AsyncCacheLoader}. If the asynchronous
1189
   * computation fails or computes a {@code null} value then the entry will be automatically
1190
   * removed. Note that multiple threads can concurrently load values for distinct keys.
1191
   * <p>
1192
   * This method does not alter the state of this {@code Caffeine} instance, so it can be invoked
1193
   * again to create multiple independent caches.
1194
   * <p>
1195
   * This construction cannot be used with {@link #weakValues()}, {@link #softValues()}, or when
1196
   * {@link #weakKeys()} are combined with {@link #evictionListener(RemovalListener)}.
1197
   *
1198
   * @param loader the cache loader used to obtain new values
1199
   * @param <K1> the key type of the loader
1200
   * @param <V1> the value type of the loader
1201
   * @return a cache having the requested features
1202
   */
1203
  @SuppressWarnings("PMD.TypeParameterNamingConventions")
1204
  public <K1 extends K, V1 extends @Nullable V> AsyncLoadingCache<K1, V1> buildAsync(
1205
      AsyncCacheLoader<? super K1, V1> loader) {
1206
    requireState(valueStrength == null,
1✔
1207
        "Weak or soft values cannot be combined with AsyncLoadingCache");
1208
    requireState(isStrongKeys() || (evictionListener == null),
1!
1209
        "Weak keys cannot be combined with eviction listener and AsyncLoadingCache");
1210
    requireWeightWithWeigher();
1✔
1211
    requireNonNull(loader);
1✔
1212

1213
    @SuppressWarnings("unchecked")
1214
    var self = (Caffeine<K1, V1>) this;
1✔
1215
    return isBounded() || refreshAfterWrite()
1✔
1216
        ? new BoundedLocalCache.BoundedLocalAsyncLoadingCache<>(self, loader)
1✔
1217
        : new UnboundedLocalCache.UnboundedLocalAsyncLoadingCache<>(self, loader);
1✔
1218
  }
1219

1220
  void requireNonLoadingCache() {
1221
    requireState(refreshAfterWriteNanos == UNSET_INT, "refreshAfterWrite requires a LoadingCache");
1✔
1222
  }
1✔
1223

1224
  void requireWeightWithWeigher() {
1225
    if (weigher == null) {
1✔
1226
      requireState(maximumWeight == UNSET_INT, "maximumWeight requires weigher");
1✔
1227
    } else if (strictParsing) {
1✔
1228
      requireState(maximumWeight != UNSET_INT, "weigher requires maximumWeight");
1!
1229
    } else if (maximumWeight == UNSET_INT) {
1!
1230
      logger.log(Level.WARNING, "ignoring weigher specified without maximumWeight");
1✔
1231
    }
1232
  }
1✔
1233

1234
  /**
1235
   * Returns a string representation for this Caffeine instance. The exact form of the returned
1236
   * string is not specified.
1237
   */
1238
  @Override
1239
  public String toString() {
1240
    var s = new StringBuilder(200)
1✔
1241
        .append(getClass().getSimpleName()).append('{');
1✔
1242
    int baseLength = s.length();
1✔
1243
    if (initialCapacity != UNSET_INT) {
1!
UNCOV
1244
      s.append("initialCapacity=").append(initialCapacity).append(", ");
×
1245
    }
1246
    if (maximumSize != UNSET_INT) {
1!
UNCOV
1247
      s.append("maximumSize=").append(maximumSize).append(", ");
×
1248
    }
1249
    if (maximumWeight != UNSET_INT) {
1!
UNCOV
1250
      s.append("maximumWeight=").append(maximumWeight).append(", ");
×
1251
    }
1252
    if (expireAfterWriteNanos != UNSET_INT) {
1!
UNCOV
1253
      s.append("expireAfterWrite=").append(expireAfterWriteNanos).append("ns, ");
×
1254
    }
1255
    if (expireAfterAccessNanos != UNSET_INT) {
1!
UNCOV
1256
      s.append("expireAfterAccess=").append(expireAfterAccessNanos).append("ns, ");
×
1257
    }
1258
    if (expiry != null) {
1!
UNCOV
1259
      s.append("expiry, ");
×
1260
    }
1261
    if (refreshAfterWriteNanos != UNSET_INT) {
1!
UNCOV
1262
      s.append("refreshAfterWrite=").append(refreshAfterWriteNanos).append("ns, ");
×
1263
    }
1264
    if (keyStrength != null) {
1!
UNCOV
1265
      s.append("keyStrength=").append(keyStrength.toString().toLowerCase(US)).append(", ");
×
1266
    }
1267
    if (valueStrength != null) {
1!
UNCOV
1268
      s.append("valueStrength=").append(valueStrength.toString().toLowerCase(US)).append(", ");
×
1269
    }
1270
    if (evictionListener != null) {
1!
UNCOV
1271
      s.append("evictionListener, ");
×
1272
    }
1273
    if (removalListener != null) {
1!
UNCOV
1274
      s.append("removalListener, ");
×
1275
    }
1276
    if (s.length() > baseLength) {
1!
UNCOV
1277
      s.delete(s.length() - 2, s.length());
×
1278
    }
1279
    return s.append('}').toString();
1✔
1280
  }
1281
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc