InternCache.java
package com.ctc.wstx.util;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Singleton class that implements "fast intern" functionality, essentially
* adding a layer that caches Strings that have been previously intern()ed,
* but that probably shouldn't be added to symbol tables.
* This is usually used by improving intern()ing of things like namespace
* URIs.
*/
public final class InternCache
{
/**
* Let's create cache big enough to usually have enough space for
* all entries... (assuming NS URIs only)
*/
private final static int DEFAULT_SIZE = 64;
/**
* Let's limit maximum size.
*/
private final static int MAX_SIZE = 1024;
private final static InternCache sInstance = new InternCache();
private final ConcurrentHashMap<String,String> mCache;
/**
* Queue tracking insertion order for FIFO eviction when the
* cache exceeds {@link #MAX_SIZE}.
*/
private final ConcurrentLinkedQueue<String> mInsertionOrder;
private InternCache() {
mCache = new ConcurrentHashMap<>(DEFAULT_SIZE);
mInsertionOrder = new ConcurrentLinkedQueue<>();
}
public static InternCache getInstance() {
return sInstance;
}
public String intern(String input)
{
// Ideally, lock-free return from cache
String result = mCache.get(input);
if (result != null) {
return result;
}
// If not found, intern the string (expensive) and attempt to add to cache
result = input.intern();
String prev = mCache.putIfAbsent(result, result);
// If another thread added the same string in the meantime, use that one
if (prev != null) {
return prev;
}
// New entry added: track insertion order and evict oldest if needed
mInsertionOrder.add(result);
while (mCache.size() > MAX_SIZE) {
String eldest = mInsertionOrder.poll();
if (eldest == null) {
// Queue is empty, should never happen in practice
break;
}
mCache.remove(eldest);
}
return result;
}
}