• 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/Interner.java
1
/*
2
 * Copyright 2022 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 java.lang.ref.Reference;
19
import java.lang.ref.ReferenceQueue;
20
import java.util.Objects;
21
import java.util.concurrent.ConcurrentHashMap;
22
import java.util.concurrent.ConcurrentMap;
23

24
import org.jspecify.annotations.NullMarked;
25
import org.jspecify.annotations.Nullable;
26

27
import com.github.benmanes.caffeine.cache.References.LookupKeyEqualsReference;
28
import com.github.benmanes.caffeine.cache.References.WeakKeyEqualsReference;
29

30
/**
31
 * Provides similar behavior to {@link String#intern} for any immutable type.
32
 * <p>
33
 * Note that {@code String.intern()} has some well-known performance limitations and should
34
 * generally be avoided. Prefer {@link Interner#newWeakInterner} or another {@code Interner}
35
 * implementation even for {@code String} interning.
36
 *
37
 * @param <E> the type of elements
38
 * @author ben.manes@gmail.com (Ben Manes)
39
 */
40
@NullMarked
41
@FunctionalInterface
42
public interface Interner<E> {
43

44
  /**
45
   * Chooses and returns the representative instance for any collection of instances that are
46
   * equal to each other. If two {@linkplain Object#equals equal} inputs are given to this method,
47
   * both calls will return the same instance. That is, {@code intern(a).equals(a)} always holds,
48
   * and {@code intern(a) == intern(b)} if and only if {@code a.equals(b)}. Note that {@code
49
   * intern(a)} is permitted to return one instance now and a different instance later if the
50
   * original interned instance was garbage-collected.
51
   * <p>
52
   * <b>Warning:</b> Do not use with mutable objects.
53
   *
54
   * @param sample the element to add if absent
55
   * @return the representative instance, possibly the {@code sample} if absent
56
   * @throws NullPointerException if {@code sample} is null
57
   */
58
  E intern(E sample);
59

60
  /**
61
   * Returns a new thread-safe interner that retains a strong reference to each instance it has
62
   * interned, thus preventing these instances from being garbage-collected.
63
   *
64
   * @param <E> the type of elements
65
   * @return an interner for retrieving the canonical instance
66
   */
67
  static <E> Interner<E> newStrongInterner() {
68
    return new StrongInterner<>();
×
69
  }
70

71
  /**
72
   * Returns a new thread-safe interner that retains a weak reference to each instance it has
73
   * interned, and so does not prevent these instances from being garbage-collected.
74
   *
75
   * @param <E> the type of elements
76
   * @return an interner for retrieving the canonical instance
77
   */
78
  static <E> Interner<E> newWeakInterner() {
79
    return new WeakInterner<>();
×
80
  }
81
}
82

83
final class StrongInterner<E> implements Interner<E> {
84
  final ConcurrentMap<E, E> map;
85

86
  StrongInterner() {
×
87
    map = new ConcurrentHashMap<>();
×
88
  }
×
89
  @Override public E intern(E sample) {
90
    E canonical = map.get(sample);
×
91
    if (canonical != null) {
×
92
      return canonical;
×
93
    }
94

95
    @Nullable E value = map.putIfAbsent(sample, sample);
×
96
    return (value == null) ? sample : value;
×
97
  }
98
}
99

100
final class WeakInterner<E> implements Interner<E> {
101
  final BoundedLocalCache<E, Boolean> cache;
102

103
  WeakInterner() {
×
104
    cache = Caffeine.newWeakInterner();
×
105
  }
×
106
  @Override public E intern(E sample) {
107
    for (;;) {
108
      E canonical = cache.getKey(sample);
×
109
      if (canonical != null) {
×
110
        return canonical;
×
111
      }
112

113
      var value = cache.putIfAbsent(sample, true);
×
114
      if (value == null) {
×
115
        return sample;
×
116
      }
117
    }
×
118
  }
119
}
120

121
@SuppressWarnings({"BooleanLiteral", "unchecked"})
122
final class Interned<K, V> extends Node<K, V> implements NodeFactory<K, V> {
123
  static final NodeFactory<Object, Object> FACTORY = new Interned<>();
×
124

125
  volatile Reference<?> keyReference;
126

127
  Interned() {
×
128
    this.keyReference = NodeFactory.DEAD_WEAK_KEY;
×
129
  }
×
130
  Interned(Reference<K> keyReference) {
×
131
    this.keyReference = keyReference;
×
132
  }
×
133
  @Override public @Nullable K getKey() {
134
    return (K) keyReference.get();
×
135
  }
136
  @Override public Object getKeyReference() {
137
    return keyReference;
×
138
  }
139
  @Override public Object getKeyReferenceOrNull() {
140
    return keyReference;
×
141
  }
142
  @Override public V getValue() {
143
    return (V) Boolean.TRUE;
×
144
  }
145
  @Override public V getValueReference() {
146
    return (V) Boolean.TRUE;
×
147
  }
148
  @Override public void setValue(V value, @Nullable ReferenceQueue<V> referenceQueue) {}
×
149
  @Override public boolean containsValue(Object value) {
150
    return Objects.equals(value, getValue());
×
151
  }
152
  @Override public Node<K, V> newNode(K key, @Nullable ReferenceQueue<K> keyReferenceQueue,
153
      V value, @Nullable ReferenceQueue<V> valueReferenceQueue, int weight, long now) {
154
    return new Interned<>(new WeakKeyEqualsReference<>(key, keyReferenceQueue));
×
155
  }
156
  @Override public Node<K, V> newNode(Object keyReference, V value,
157
      @Nullable ReferenceQueue<V> valueReferenceQueue, int weight, long now) {
158
    return new Interned<>((Reference<K>) keyReference);
×
159
  }
160
  @Override public Object newLookupKey(Object key) {
161
    return new LookupKeyEqualsReference<>(key);
×
162
  }
163
  @Override public Object newReferenceKey(K key, ReferenceQueue<K> referenceQueue) {
164
    return new WeakKeyEqualsReference<>(key, referenceQueue);
×
165
  }
166
  @Override public boolean isAlive() {
167
    Object keyRef = keyReference;
×
168
    return (keyRef != RETIRED_WEAK_KEY) && (keyRef != DEAD_WEAK_KEY);
×
169
  }
170
  @Override public boolean isRetired() {
171
    return (keyReference == RETIRED_WEAK_KEY);
×
172
  }
173
  @Override public void retire() {
174
    var keyRef = keyReference;
×
175
    keyReference = RETIRED_WEAK_KEY;
×
176
    keyRef.clear();
×
177
  }
×
178
  @Override public boolean isDead() {
179
    return (keyReference == DEAD_WEAK_KEY);
×
180
  }
181
  @Override public void die() {
182
    var keyRef = keyReference;
×
183
    keyReference = DEAD_WEAK_KEY;
×
184
    keyRef.clear();
×
185
  }
×
186
}
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