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

ben-manes / caffeine / #5173

29 Dec 2025 05:27AM UTC coverage: 0.0% (-100.0%) from 100.0%
#5173

push

github

ben-manes
speed up development ci build

0 of 3838 branches covered (0.0%)

0 of 7869 relevant lines covered (0.0%)

0.0 hits per line

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

0.0
/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, and the deserialized caches retain all
124
 * the configuration properties of the original cache. Note that the serialized form does <i>not</i>
125
 * include cache contents but only configuration.
126
 *
127
 * @author ben.manes@gmail.com (Ben Manes)
128
 * @param <K> the most general key type this builder will be able to create caches for. This is
129
 *     normally {@code Object} unless it is constrained by using a method like {@code
130
 *     #removalListener}
131
 * @param <V> the most general value 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
 */
135
@NullMarked
136
@SuppressWarnings({"JavadocDeclaration", "JavadocReference"})
137
public final class Caffeine<K, V> {
138
  static final Supplier<StatsCounter> ENABLED_STATS_COUNTER_SUPPLIER = ConcurrentStatsCounter::new;
×
139
  static final Logger logger = System.getLogger(Caffeine.class.getName());
×
140
  static final Duration MIN_DURATION = Duration.ofNanos(Long.MIN_VALUE);
×
141
  static final Duration MAX_DURATION = Duration.ofNanos(Long.MAX_VALUE);
×
142
  static final double DEFAULT_LOAD_FACTOR = 0.75;
143
  static final int DEFAULT_INITIAL_CAPACITY = 16;
144

145
  enum Strength { WEAK, SOFT }
×
146
  static final int UNSET_INT = -1;
147

148
  boolean strictParsing = true;
×
149
  boolean interner;
150

151
  long maximumSize = UNSET_INT;
×
152
  long maximumWeight = UNSET_INT;
×
153
  int initialCapacity = UNSET_INT;
×
154

155
  long expireAfterWriteNanos = UNSET_INT;
×
156
  long expireAfterAccessNanos = UNSET_INT;
×
157
  long refreshAfterWriteNanos = UNSET_INT;
×
158

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

168
  @Nullable Strength keyStrength;
169
  @Nullable Strength valueStrength;
170

171
  private Caffeine() {}
×
172

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

181
  /** Ensures that the argument expression is true. */
182
  static void requireArgument(boolean expression, String message) {
183
    if (!expression) {
×
184
      throw new IllegalArgumentException(message);
×
185
    }
186
  }
×
187

188
  /** Ensures that the argument expression is true. */
189
  static void requireArgument(boolean expression) {
190
    if (!expression) {
×
191
      throw new IllegalArgumentException();
×
192
    }
193
  }
×
194

195
  /** Ensures that the state expression is true. */
196
  static void requireState(boolean expression) {
197
    if (!expression) {
×
198
      throw new IllegalStateException();
×
199
    }
200
  }
×
201

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

210
  /** Returns the smallest power of two greater than or equal to {@code x}. */
211
  static int ceilingPowerOfTwo(int x) {
212
    // From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
213
    return 1 << -Integer.numberOfLeadingZeros(x - 1);
×
214
  }
215

216
  /** Returns the smallest power of two greater than or equal to {@code x}. */
217
  static long ceilingPowerOfTwo(long x) {
218
    // From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
219
    return 1L << -Long.numberOfLeadingZeros(x - 1);
×
220
  }
221

222
  /**
223
   * Calculate initial capacity for {@link HashMap}-based classes, from expected size and default
224
   * load factor (0.75).
225
   *
226
   * @param numMappings the expected number of mappings
227
   * @return initial capacity for HashMap based classes
228
   */
229
  static int calculateHashMapCapacity(int numMappings) {
230
    return (int) Math.ceil(numMappings / DEFAULT_LOAD_FACTOR);
×
231
  }
232

233
  /**
234
   * Calculate initial capacity for {@link HashMap}-based classes, from expected size and default
235
   * load factor (0.75).
236
   *
237
   * @param iterable the expected number of mappings
238
   * @return initial capacity for HashMap based classes
239
   */
240
  static int calculateHashMapCapacity(Iterable<?> iterable) {
241
    return (iterable instanceof Collection)
×
242
        ? calculateHashMapCapacity(((Collection<?>) iterable).size())
×
243
        : DEFAULT_INITIAL_CAPACITY;
×
244
  }
245

246
  /**
247
   * Returns the number of nanoseconds of the given duration without throwing or overflowing.
248
   * <p>
249
   * Instead of throwing {@link ArithmeticException}, this method silently saturates to either
250
   * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing
251
   * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair.
252
   */
253
  static long toNanosSaturated(Duration duration) {
254
    return duration.isNegative()
×
255
        ? (duration.compareTo(MIN_DURATION) <= 0) ? Long.MIN_VALUE : duration.toNanos()
×
256
        : (duration.compareTo(MAX_DURATION) >= 0) ? Long.MAX_VALUE : duration.toNanos();
×
257
  }
258

259
  /** Returns whether the instance has implemented a method for enhanced functionality. */
260
  static boolean hasMethodOverride(Class<?> clazz,
261
      Object instance, String methodName, Class<?>... parameterTypes) {
262
    try {
263
      Method instanceMethod = instance.getClass().getMethod(methodName, parameterTypes);
×
264
      Method classMethod = clazz.getMethod(methodName, parameterTypes);
×
265
      return !instanceMethod.equals(classMethod);
×
266
    } catch (NoSuchMethodException | SecurityException e) {
×
267
      logger.log(Level.WARNING, "Cannot determine if {0} overrides {1}({2})",
×
268
          instance.getClass().getSimpleName(), methodName, Arrays.toString(parameterTypes), e);
×
269
      return false;
×
270
    }
271
  }
272

273
  /**
274
   * Constructs a new {@code Caffeine} instance with default settings, including strong keys, strong
275
   * values, and no automatic eviction of any kind.
276
   * <p>
277
   * Note that while this return type is {@code Caffeine<Object, Object>}, type parameters on the
278
   * {@link #build} methods allow you to create a cache of any key and value type desired.
279
   *
280
   * @return a new instance with default settings
281
   */
282
  public static Caffeine<Object, Object> newBuilder() {
283
    return new Caffeine<>();
×
284
  }
285

286
  /** Returns a cache that is optimized for weak reference interning (see {@link Interner}). */
287
  static <K> BoundedLocalCache<K, Boolean> newWeakInterner() {
288
    var builder = new Caffeine<K, Boolean>().executor(Runnable::run).weakKeys();
×
289
    builder.interner = true;
×
290
    return LocalCacheFactory.newBoundedLocalCache(builder,
×
291
        /* cacheLoader= */ null, /* isAsync= */ false);
292
  }
293

294
  /**
295
   * Constructs a new {@code Caffeine} instance with the settings specified in {@code spec}.
296
   *
297
   * @param spec the specification to build from
298
   * @return a new instance with the specification's settings
299
   */
300
  public static Caffeine<Object, Object> from(CaffeineSpec spec) {
301
    Caffeine<Object, Object> builder = spec.toBuilder();
×
302
    builder.strictParsing = false;
×
303
    return builder;
×
304
  }
305

306
  /**
307
   * Constructs a new {@code Caffeine} instance with the settings specified in {@code spec}.
308
   *
309
   * @param spec a String in the format specified by {@link CaffeineSpec}
310
   * @return a new instance with the specification's settings
311
   */
312
  public static Caffeine<Object, Object> from(String spec) {
313
    return from(CaffeineSpec.parse(spec));
×
314
  }
315

316
  /**
317
   * Sets the minimum total size for the internal data structures. Providing a large enough estimate
318
   * at construction time avoids the need for expensive resizing operations later, but setting this
319
   * value unnecessarily high wastes memory.
320
   *
321
   * @param initialCapacity minimum total size for the internal data structures
322
   * @return this {@code Caffeine} instance (for chaining)
323
   * @throws IllegalArgumentException if {@code initialCapacity} is negative
324
   * @throws IllegalStateException if an initial capacity was already set
325
   */
326
  @CanIgnoreReturnValue
327
  public Caffeine<K, V> initialCapacity(int initialCapacity) {
328
    requireState(this.initialCapacity == UNSET_INT,
×
329
        "initial capacity was already set to %s", this.initialCapacity);
×
330
    requireArgument(initialCapacity >= 0);
×
331
    this.initialCapacity = initialCapacity;
×
332
    return this;
×
333
  }
334

335
  boolean hasInitialCapacity() {
336
    return (initialCapacity != UNSET_INT);
×
337
  }
338

339
  int getInitialCapacity() {
340
    return hasInitialCapacity() ? initialCapacity : DEFAULT_INITIAL_CAPACITY;
×
341
  }
342

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

367
  Executor getExecutor() {
368
    return (executor == null) ? ForkJoinPool.commonPool() : executor;
×
369
  }
370

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

396
  Scheduler getScheduler() {
397
    if ((scheduler == null) || (scheduler == Scheduler.disabledScheduler())) {
×
398
      return Scheduler.disabledScheduler();
×
399
    } else if (scheduler == Scheduler.systemScheduler()) {
×
400
      return scheduler;
×
401
    }
402
    return Scheduler.guardedScheduler(scheduler);
×
403
  }
404

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

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

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

511
    @SuppressWarnings("unchecked")
512
    var self = (Caffeine<K1, V1>) this;
×
513
    self.weigher = weigher;
×
514
    return self;
×
515
  }
516

517
  boolean evicts() {
518
    return getMaximum() != UNSET_INT;
×
519
  }
520

521
  boolean isWeighted() {
522
    return (weigher != null);
×
523
  }
524

525
  long getMaximum() {
526
    return isWeighted() ? maximumWeight : maximumSize;
×
527
  }
528

529
  @SuppressWarnings({"JavaAnnotator", "PMD.TypeParameterNamingConventions", "unchecked"})
530
  <K1 extends K, V1 extends V> Weigher<K1, V1> getWeigher(boolean isAsync) {
531
    Weigher<K1, V1> delegate = (weigher == null) || (weigher == Weigher.singletonWeigher())
×
532
        ? Weigher.singletonWeigher()
×
533
        : Weigher.boundedWeigher((Weigher<K1, V1>) weigher);
×
534
    return isAsync ? (Weigher<K1, V1>) new AsyncWeigher<>(delegate) : delegate;
×
535
  }
536

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

563
  boolean isStrongKeys() {
564
    return (keyStrength == null);
×
565
  }
566

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

593
  boolean isStrongValues() {
594
    return (valueStrength == null);
×
595
  }
596

597
  boolean isWeakValues() {
598
    return (valueStrength == Strength.WEAK);
×
599
  }
600

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

630
  /**
631
   * Specifies that each entry should be automatically removed from the cache once a fixed duration
632
   * has elapsed after the entry's creation, or the most recent replacement of its value.
633
   * <p>
634
   * Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible to
635
   * read or write operations. Expired entries are cleaned up as part of the routine maintenance
636
   * described in the class Javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt
637
   * removal of expired entries.
638
   *
639
   * @param duration the length of time after an entry is created or updated before it should be
640
   *        automatically removed
641
   * @return this {@code Caffeine} instance (for chaining)
642
   * @throws IllegalArgumentException if {@code duration} is negative
643
   * @throws IllegalStateException if the time to live or variable expiration was already set
644
   * @throws ArithmeticException for durations greater than +/- approximately 292 years
645
   */
646
  @CanIgnoreReturnValue
647
  public Caffeine<K, V> expireAfterWrite(Duration duration) {
648
    return expireAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS);
×
649
  }
650

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

680
  long getExpiresAfterWriteNanos() {
681
    return expireAfterWriteNanos;
×
682
  }
683

684
  boolean expiresAfterWrite() {
685
    return (expireAfterWriteNanos != UNSET_INT);
×
686
  }
687

688
  /**
689
   * Specifies that each entry should be automatically removed from the cache once a fixed duration
690
   * has elapsed after the entry's creation, the most recent replacement of its value, or its last
691
   * access. Access time is reset by all cache read and write operations (including {@code
692
   * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by operations on the
693
   * collection-views of {@link Cache#asMap}.
694
   * <p>
695
   * Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible to
696
   * read or write operations. Expired entries are cleaned up as part of the routine maintenance
697
   * described in the class Javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt
698
   * removal of expired entries.
699
   *
700
   * @param duration the length of time after an entry is last accessed before it should be
701
   *        automatically removed
702
   * @return this {@code Caffeine} instance (for chaining)
703
   * @throws IllegalArgumentException if {@code duration} is negative
704
   * @throws IllegalStateException if the time to idle or variable expiration was already set
705
   * @throws ArithmeticException for durations greater than +/- approximately 292 years
706
   */
707
  @CanIgnoreReturnValue
708
  public Caffeine<K, V> expireAfterAccess(Duration duration) {
709
    return expireAfterAccess(toNanosSaturated(duration), TimeUnit.NANOSECONDS);
×
710
  }
711

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

744
  long getExpiresAfterAccessNanos() {
745
    return expireAfterAccessNanos;
×
746
  }
747

748
  boolean expiresAfterAccess() {
749
    return (expireAfterAccessNanos != UNSET_INT);
×
750
  }
751

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

790
    @SuppressWarnings("unchecked")
791
    var self = (Caffeine<K1, V1>) this;
×
792
    self.expiry = expiry;
×
793
    return self;
×
794
  }
795

796
  boolean expiresVariable() {
797
    return expiry != null;
×
798
  }
799

800
  @SuppressWarnings("unchecked")
801
  @Nullable Expiry<K, V> getExpiry(boolean isAsync) {
802
    return isAsync && (expiry != null)
×
803
        ? (Expiry<K, V>) new AsyncExpiry<>(expiry)
×
804
        : (Expiry<K, V>) expiry;
×
805
  }
806

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

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

865
  long getRefreshAfterWriteNanos() {
866
    return refreshAfterWriteNanos;
×
867
  }
868

869
  boolean refreshAfterWrite() {
870
    return refreshAfterWriteNanos != UNSET_INT;
×
871
  }
872

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

893
  Ticker getTicker() {
894
    boolean useTicker = expiresVariable() || expiresAfterAccess()
×
895
        || expiresAfterWrite() || refreshAfterWrite();
×
896
    return useTicker
×
897
        ? (ticker == null) ? Ticker.systemTicker() : ticker
×
898
        : Ticker.disabledTicker();
×
899
  }
900

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

942
    @SuppressWarnings("unchecked")
943
    var self = (Caffeine<K1, V1>) this;
×
944
    self.evictionListener = requireNonNull(evictionListener);
×
945
    return self;
×
946
  }
947

948
  @SuppressWarnings({"JavaAnnotator", "PMD.TypeParameterNamingConventions", "unchecked"})
949
  <K1 extends K, V1 extends V> @Nullable RemovalListener<K1, V1> getEvictionListener(
950
      boolean async) {
951
    var castedListener = (RemovalListener<K1, V1>) evictionListener;
×
952
    return async && (castedListener != null)
×
953
        ? (RemovalListener<K1, V1>) new AsyncEvictionListener<>(castedListener)
×
954
        : castedListener;
×
955
  }
956

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

995
    @SuppressWarnings("unchecked")
996
    var self = (Caffeine<K1, V1>) this;
×
997
    self.removalListener = requireNonNull(removalListener);
×
998
    return self;
×
999
  }
1000

1001
  @SuppressWarnings({"JavaAnnotator", "PMD.TypeParameterNamingConventions", "unchecked"})
1002
  <K1 extends K, V1 extends V> @Nullable RemovalListener<K1, V1> getRemovalListener(boolean async) {
1003
    var castedListener = (RemovalListener<K1, V1>) removalListener;
×
1004
    return async && (castedListener != null)
×
1005
        ? (RemovalListener<K1, V1>) new AsyncRemovalListener<>(castedListener, getExecutor())
×
1006
        : castedListener;
×
1007
  }
1008

1009
  /**
1010
   * Enables the accumulation of {@link CacheStats} during the operation of the cache. Without this
1011
   * {@link Cache#stats} will return zero for all statistics. Note that recording statistics
1012
   * requires bookkeeping to be performed with each operation, and thus imposes a performance
1013
   * penalty on cache operations.
1014
   *
1015
   * @return this {@code Caffeine} instance (for chaining)
1016
   */
1017
  @CanIgnoreReturnValue
1018
  public Caffeine<K, V> recordStats() {
1019
    requireState(this.statsCounterSupplier == null, "Statistics recording was already set");
×
1020
    statsCounterSupplier = ENABLED_STATS_COUNTER_SUPPLIER;
×
1021
    return this;
×
1022
  }
1023

1024
  /**
1025
   * Enables the accumulation of {@link CacheStats} during the operation of the cache. Without this
1026
   * {@link Cache#stats} will return zero for all statistics. Note that recording statistics
1027
   * requires bookkeeping to be performed with each operation, and thus imposes a performance
1028
   * penalty on cache operations. Any exception thrown by the supplied {@link StatsCounter} will be
1029
   * suppressed and logged.
1030
   *
1031
   * @param statsCounterSupplier a supplier instance that returns a new {@link StatsCounter}
1032
   * @return this {@code Caffeine} instance (for chaining)
1033
   */
1034
  @CanIgnoreReturnValue
1035
  public Caffeine<K, V> recordStats(Supplier<? extends StatsCounter> statsCounterSupplier) {
1036
    requireState(this.statsCounterSupplier == null, "Statistics recording was already set");
×
1037
    requireNonNull(statsCounterSupplier);
×
1038
    this.statsCounterSupplier = () -> StatsCounter.guardedStatsCounter(statsCounterSupplier.get());
×
1039
    return this;
×
1040
  }
1041

1042
  boolean isRecordingStats() {
1043
    return (statsCounterSupplier != null);
×
1044
  }
1045

1046
  Supplier<StatsCounter> getStatsCounterSupplier() {
1047
    return (statsCounterSupplier == null)
×
1048
        ? StatsCounter::disabledStatsCounter
×
1049
        : statsCounterSupplier;
×
1050
  }
1051

1052
  boolean isBounded() {
1053
    return (maximumSize != UNSET_INT)
×
1054
        || (maximumWeight != UNSET_INT)
1055
        || (expireAfterAccessNanos != UNSET_INT)
1056
        || (expireAfterWriteNanos != UNSET_INT)
1057
        || (expiry != null)
1058
        || (keyStrength != null)
1059
        || (valueStrength != null);
1060
  }
1061

1062
  /**
1063
   * Builds a cache which does not automatically load values when keys are requested unless a
1064
   * mapping function is provided. Note that multiple threads can concurrently load values for
1065
   * distinct keys.
1066
   * <p>
1067
   * Consider {@link #build(CacheLoader)} instead, if it is feasible to implement a
1068
   * {@code CacheLoader}.
1069
   * <p>
1070
   * This method does not alter the state of this {@code Caffeine} instance, so it can be invoked
1071
   * again to create multiple independent caches.
1072
   *
1073
   * @param <K1> the key type of the cache
1074
   * @param <V1> the value type of the cache
1075
   * @return a cache having the requested features
1076
   */
1077
  @SuppressWarnings("PMD.TypeParameterNamingConventions")
1078
  public <K1 extends K, V1 extends @Nullable V> Cache<K1, V1> build() {
1079
    requireWeightWithWeigher();
×
1080
    requireNonLoadingCache();
×
1081

1082
    @SuppressWarnings("unchecked")
1083
    var self = (Caffeine<K1, V1>) this;
×
1084
    return isBounded()
×
1085
        ? new BoundedLocalCache.BoundedLocalManualCache<>(self)
×
1086
        : new UnboundedLocalCache.UnboundedLocalManualCache<>(self);
×
1087
  }
1088

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

1108
    @SuppressWarnings("unchecked")
1109
    var self = (Caffeine<K1, V1>) this;
×
1110
    return isBounded() || refreshAfterWrite()
×
1111
        ? new BoundedLocalCache.BoundedLocalLoadingCache<>(self, loader)
×
1112
        : new UnboundedLocalCache.UnboundedLocalLoadingCache<>(self, loader);
×
1113
  }
1114

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

1143
    @SuppressWarnings("unchecked")
1144
    var self = (Caffeine<K1, V1>) this;
×
1145
    return isBounded()
×
1146
        ? new BoundedLocalCache.BoundedLocalAsyncCache<>(self)
×
1147
        : new UnboundedLocalCache.UnboundedLocalAsyncCache<>(self);
×
1148
  }
1149

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

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

1202
    @SuppressWarnings("unchecked")
1203
    var self = (Caffeine<K1, V1>) this;
×
1204
    return isBounded() || refreshAfterWrite()
×
1205
        ? new BoundedLocalCache.BoundedLocalAsyncLoadingCache<>(self, loader)
×
1206
        : new UnboundedLocalCache.UnboundedLocalAsyncLoadingCache<>(self, loader);
×
1207
  }
1208

1209
  void requireNonLoadingCache() {
1210
    requireState(refreshAfterWriteNanos == UNSET_INT, "refreshAfterWrite requires a LoadingCache");
×
1211
  }
×
1212

1213
  void requireWeightWithWeigher() {
1214
    if (weigher == null) {
×
1215
      requireState(maximumWeight == UNSET_INT, "maximumWeight requires weigher");
×
1216
    } else if (strictParsing) {
×
1217
      requireState(maximumWeight != UNSET_INT, "weigher requires maximumWeight");
×
1218
    } else if (maximumWeight == UNSET_INT) {
×
1219
      logger.log(Level.WARNING, "ignoring weigher specified without maximumWeight");
×
1220
    }
1221
  }
×
1222

1223
  /**
1224
   * Returns a string representation for this Caffeine instance. The exact form of the returned
1225
   * string is not specified.
1226
   */
1227
  @Override
1228
  public String toString() {
1229
    var s = new StringBuilder(200)
×
1230
        .append(getClass().getSimpleName()).append('{');
×
1231
    int baseLength = s.length();
×
1232
    if (initialCapacity != UNSET_INT) {
×
1233
      s.append("initialCapacity=").append(initialCapacity).append(", ");
×
1234
    }
1235
    if (maximumSize != UNSET_INT) {
×
1236
      s.append("maximumSize=").append(maximumSize).append(", ");
×
1237
    }
1238
    if (maximumWeight != UNSET_INT) {
×
1239
      s.append("maximumWeight=").append(maximumWeight).append(", ");
×
1240
    }
1241
    if (expireAfterWriteNanos != UNSET_INT) {
×
1242
      s.append("expireAfterWrite=").append(expireAfterWriteNanos).append("ns, ");
×
1243
    }
1244
    if (expireAfterAccessNanos != UNSET_INT) {
×
1245
      s.append("expireAfterAccess=").append(expireAfterAccessNanos).append("ns, ");
×
1246
    }
1247
    if (expiry != null) {
×
1248
      s.append("expiry, ");
×
1249
    }
1250
    if (refreshAfterWriteNanos != UNSET_INT) {
×
1251
      s.append("refreshAfterWrite=").append(refreshAfterWriteNanos).append("ns, ");
×
1252
    }
1253
    if (keyStrength != null) {
×
1254
      s.append("keyStrength=").append(keyStrength.toString().toLowerCase(US)).append(", ");
×
1255
    }
1256
    if (valueStrength != null) {
×
1257
      s.append("valueStrength=").append(valueStrength.toString().toLowerCase(US)).append(", ");
×
1258
    }
1259
    if (evictionListener != null) {
×
1260
      s.append("evictionListener, ");
×
1261
    }
1262
    if (removalListener != null) {
×
1263
      s.append("removalListener, ");
×
1264
    }
1265
    if (s.length() > baseLength) {
×
1266
      s.delete(s.length() - 2, s.length());
×
1267
    }
1268
    return s.append('}').toString();
×
1269
  }
1270
}
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