This commit is contained in:
Looly 2022-04-17 16:19:16 +08:00
parent 7e36d0f076
commit 992477f521
26 changed files with 787 additions and 162 deletions

View File

@ -13,9 +13,12 @@
* 【json 】 新增JSONParser
* 【json 】 JSON新增在解析时的过滤方法issue#I52O85@Gitee
* 【core 】 添加ArrayUtil.distinct、CollUtil.distinct重载issue#2256@Github
* 【core 】 添加TransMap、FuncMap、ReferenceConcurrentMap、WeakConcurrentMap
### 🐞Bug修复
* 【core 】 修复StrUtil.firstNonX非static问题issue#2257@Github
* 【core 】 修复SimpleCache线程安全问题
* 【core 】 修复ClassLoaderUtil中可能的关联ClassLoader错位问题
-------------------------------------------------------------------------------------------------------------

View File

@ -1,7 +1,7 @@
package cn.hutool.core.bean;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.map.WeakConcurrentMap;
/**
* Bean属性缓存<br>
@ -12,7 +12,7 @@ import cn.hutool.core.lang.func.Func0;
public enum BeanDescCache {
INSTANCE;
private final SimpleCache<Class<?>, BeanDesc> bdCache = new SimpleCache<>();
private final WeakConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakConcurrentMap<>();
/**
* 获得属性名和{@link BeanDesc}Map映射
@ -23,7 +23,7 @@ public enum BeanDescCache {
* @since 5.4.2
*/
public BeanDesc getBeanDesc(Class<?> beanClass, Func0<BeanDesc> supplier) {
return bdCache.get(beanClass, supplier);
return bdCache.computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException());
}
/**

View File

@ -1,7 +1,8 @@
package cn.hutool.core.bean;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.map.ReferenceConcurrentMap;
import cn.hutool.core.map.WeakConcurrentMap;
import java.beans.PropertyDescriptor;
import java.util.Map;
@ -15,8 +16,8 @@ import java.util.Map;
public enum BeanInfoCache {
INSTANCE;
private final SimpleCache<Class<?>, Map<String, PropertyDescriptor>> pdCache = new SimpleCache<>();
private final SimpleCache<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new SimpleCache<>();
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> pdCache = new WeakConcurrentMap<>();
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new WeakConcurrentMap<>();
/**
* 获得属性名和{@link PropertyDescriptor}Map映射
@ -42,7 +43,7 @@ public enum BeanInfoCache {
Class<?> beanClass,
boolean ignoreCase,
Func0<Map<String, PropertyDescriptor>> supplier) {
return getCache(ignoreCase).get(beanClass, supplier);
return getCache(ignoreCase).computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException());
}
/**
@ -70,10 +71,10 @@ public enum BeanInfoCache {
* 根据是否忽略字段名的大小写返回不用Cache对象
*
* @param ignoreCase 是否忽略大小写
* @return SimpleCache
* @return {@link ReferenceConcurrentMap}
* @since 5.4.1
*/
private SimpleCache<Class<?>, Map<String, PropertyDescriptor>> getCache(boolean ignoreCase) {
private ReferenceConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> getCache(boolean ignoreCase) {
return ignoreCase ? ignoreCasePdCache : pdCache;
}
}

View File

@ -3,8 +3,8 @@ package cn.hutool.core.convert.impl;
import cn.hutool.core.convert.AbstractConverter;
import cn.hutool.core.convert.ConvertException;
import cn.hutool.core.lang.EnumItem;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ModifierUtil;
@ -25,7 +25,7 @@ import java.util.stream.Collectors;
public class EnumConverter extends AbstractConverter<Object> {
private static final long serialVersionUID = 1L;
private static final SimpleCache<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new SimpleCache<>();
private static final WeakConcurrentMap<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new WeakConcurrentMap<>();
private final Class enumClass;
@ -132,7 +132,7 @@ public class EnumConverter extends AbstractConverter<Object> {
* @return 转换方法mapkey为方法参数类型value为方法
*/
private static Map<Class<?>, Method> getMethodMap(Class<?> enumClass) {
return VALUE_OF_METHOD_CACHE.get(enumClass, () -> Arrays.stream(enumClass.getMethods())
return VALUE_OF_METHOD_CACHE.computeIfAbsent(enumClass, (key) -> Arrays.stream(enumClass.getMethods())
.filter(ModifierUtil::isStatic)
.filter(m -> m.getReturnType() == enumClass)
.filter(m -> m.getParameterCount() == 1)

View File

@ -1,5 +1,7 @@
package cn.hutool.core.lang;
import cn.hutool.core.map.WeakConcurrentMap;
import java.util.regex.Pattern;
/**
@ -174,7 +176,7 @@ public class PatternPool {
/**
* Pattern池
*/
private static final SimpleCache<RegexWithFlag, Pattern> POOL = new SimpleCache<>();
private static final WeakConcurrentMap<RegexWithFlag, Pattern> POOL = new WeakConcurrentMap<>();
/**
* 先从Pattern池中查找正则对应的{@link Pattern}找不到则编译正则表达式并入池
@ -195,7 +197,7 @@ public class PatternPool {
*/
public static Pattern get(String regex, int flags) {
final RegexWithFlag regexWithFlag = new RegexWithFlag(regex, flags);
return POOL.get(regexWithFlag, ()-> Pattern.compile(regex, flags));
return POOL.computeIfAbsent(regexWithFlag, (key)-> Pattern.compile(regex, flags));
}
/**

View File

@ -4,6 +4,7 @@ import cn.hutool.core.collection.TransIter;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.lang.mutable.Mutable;
import cn.hutool.core.lang.mutable.MutableObj;
import cn.hutool.core.map.WeakConcurrentMap;
import java.io.Serializable;
import java.util.Iterator;
@ -11,12 +12,13 @@ import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Predicate;
/**
* 简单缓存无超时实现默认使用{@link WeakHashMap}实现缓存自动清理
* 简单缓存无超时实现默认使用{@link WeakConcurrentMap}实现缓存自动清理
*
* @param <K> 键类型
* @param <V> 值类型
@ -30,7 +32,7 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
*/
private final Map<Mutable<K>, V> rawMap;
// 乐观读写锁
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
/**
* 写的时候每个key一把锁降低锁的粒度
*/
@ -40,7 +42,7 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
* 构造默认使用{@link WeakHashMap}实现缓存自动清理
*/
public SimpleCache() {
this(new WeakHashMap<>());
this(new WeakConcurrentMap<>());
}
/**
@ -94,6 +96,9 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
*/
public V get(K key, Predicate<V> validPredicate, Func0<V> supplier) {
V v = get(key);
if((null != validPredicate && false == validPredicate.test(v))){
v = null;
}
if (null == v && null != supplier) {
//每个key单独获取一把锁降低锁的粒度提高并发能力see pr#1385@Github
final Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock());

View File

@ -6,7 +6,7 @@ import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
* 单例类<br>
@ -16,7 +16,7 @@ import java.util.HashMap;
*/
public final class Singleton {
private static final SimpleCache<String, Object> POOL = new SimpleCache<>(new HashMap<>());
private static final ConcurrentHashMap<String, Object> POOL = new ConcurrentHashMap<>();
private Singleton() {
}
@ -50,7 +50,7 @@ public final class Singleton {
*/
@SuppressWarnings("unchecked")
public static <T> T get(String key, Func0<T> supplier) {
return (T) POOL.get(key, supplier::call);
return (T) POOL.computeIfAbsent(key, (k)-> supplier.callWithRuntimeException());
}
/**

View File

@ -1,7 +1,7 @@
package cn.hutool.core.lang.func;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
@ -18,7 +18,7 @@ import java.lang.invoke.SerializedLambda;
*/
public class LambdaUtil {
private static final SimpleCache<String, SerializedLambda> cache = new SimpleCache<>();
private static final WeakConcurrentMap<String, SerializedLambda> cache = new WeakConcurrentMap<>();
/**
* 通过对象的方法或类的静态方法引用获取lambda实现类
@ -202,7 +202,7 @@ public class LambdaUtil {
* @return 返回解析后的结果
*/
private static SerializedLambda _resolve(Serializable func) {
return cache.get(func.getClass().getName(), () -> ReflectUtil.invoke(func, "writeReplace"));
return cache.computeIfAbsent(func.getClass().getName(), (key) -> ReflectUtil.invoke(func, "writeReplace"));
}
//endregion
}

View File

@ -1,6 +1,6 @@
package cn.hutool.core.lang.intern;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.map.WeakConcurrentMap;
/**
* 使用WeakHashMap(线程安全)存储对象的规范化对象注意此对象需单例使用<br>
@ -10,13 +10,13 @@ import cn.hutool.core.lang.SimpleCache;
*/
public class WeakInterner<T> implements Interner<T>{
private final SimpleCache<T, T> cache = new SimpleCache<>();
private final WeakConcurrentMap<T, T> cache = new WeakConcurrentMap<>();
@Override
public T intern(T sample) {
if(null == sample){
return null;
}
return cache.get(sample, ()->sample);
return cache.computeIfAbsent(sample, (key)->sample);
}
}

View File

@ -1,7 +1,7 @@
package cn.hutool.core.lang.reflect;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.util.TypeUtil;
import java.lang.reflect.ParameterizedType;
@ -18,7 +18,7 @@ import java.util.Map;
*/
public class ActualTypeMapperPool {
private static final SimpleCache<Type, Map<Type, Type>> CACHE = new SimpleCache<>();
private static final WeakConcurrentMap<Type, Map<Type, Type>> CACHE = new WeakConcurrentMap<>();
/**
* 获取泛型变量和泛型实际类型的对应关系Map
@ -27,7 +27,7 @@ public class ActualTypeMapperPool {
* @return 泛型对应关系Map
*/
public static Map<Type, Type> get(Type type) {
return CACHE.get(type, () -> createTypeMap(type));
return CACHE.computeIfAbsent(type, (key) -> createTypeMap(type));
}
/**

View File

@ -1,7 +1,6 @@
package cn.hutool.core.map;
import java.util.Map;
import java.util.function.BiFunction;
/**
* 自定义键的Map默认HashMap实现
@ -11,7 +10,7 @@ import java.util.function.BiFunction;
* @author Looly
* @since 4.0.7
*/
public abstract class CustomKeyMap<K, V> extends MapWrapper<K, V> {
public abstract class CustomKeyMap<K, V> extends TransMap<K, V> {
private static final long serialVersionUID = 4043263744224569870L;
/**
@ -26,78 +25,8 @@ public abstract class CustomKeyMap<K, V> extends MapWrapper<K, V> {
}
@Override
public V get(Object key) {
return super.get(customKey(key));
}
@SuppressWarnings("unchecked")
@Override
public V put(K key, V value) {
return super.put((K) customKey(key), value);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
m.forEach(this::put);
}
@Override
public boolean containsKey(Object key) {
return super.containsKey(customKey(key));
}
@Override
public V remove(Object key) {
return super.remove(customKey(key));
}
@Override
public boolean remove(Object key, Object value) {
return super.remove(customKey(key), value);
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
protected V customValue(Object value) {
//noinspection unchecked
return super.replace((K) customKey(key), oldValue, newValue);
return (V)value;
}
@Override
public V replace(K key, V value) {
//noinspection unchecked
return super.replace((K) customKey(key), value);
}
//---------------------------------------------------------------------------- Override default methods start
@Override
public V getOrDefault(Object key, V defaultValue) {
return super.getOrDefault(customKey(key), defaultValue);
}
@Override
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
//noinspection unchecked
return super.computeIfPresent((K) customKey(key), remappingFunction);
}
@Override
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
//noinspection unchecked
return super.compute((K) customKey(key), remappingFunction);
}
@Override
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
//noinspection unchecked
return super.merge((K) customKey(key), value, remappingFunction);
}
//---------------------------------------------------------------------------- Override default methods end
/**
* 自定义键
*
* @param key KEY
* @return 自定义KEY
*/
protected abstract Object customKey(Object key);
}

View File

@ -38,10 +38,11 @@ public class FuncKeyMap<K, V> extends CustomKeyMap<K, V> {
* @return 驼峰Key
*/
@Override
protected Object customKey(Object key) {
protected K customKey(Object key) {
if (null != this.keyFunc) {
return keyFunc.apply(key);
}
return key;
//noinspection unchecked
return (K)key;
}
}

View File

@ -0,0 +1,73 @@
package cn.hutool.core.map;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 自定义键值函数风格的Map
*
* @param <K> 键类型
* @param <V> 值类型
* @author Looly
* @since 5.8.0
*/
public class FuncMap<K, V> extends TransMap<K, V> {
private static final long serialVersionUID = 1L;
private final Function<Object, K> keyFunc;
private final Function<Object, V> valueFunc;
// ------------------------------------------------------------------------- Constructor start
/**
* 构造<br>
* 注意提供的Map中不能有键值对否则可能导致自定义key失效
*
* @param mapFactory Map提供的空map
* @param keyFunc 自定义KEY的函数
* @param valueFunc 自定义value函数
*/
public FuncMap(Supplier<Map<K, V>> mapFactory, Function<Object, K> keyFunc, Function<Object, V> valueFunc) {
this(mapFactory.get(), keyFunc, valueFunc);
}
/**
* 构造<br>
* 注意提供的Map中不能有键值对否则可能导致自定义key失效
*
* @param emptyMap Map提供的空map
* @param keyFunc 自定义KEY的函数
* @param valueFunc 自定义value函数
*/
public FuncMap(Map<K, V> emptyMap, Function<Object, K> keyFunc, Function<Object, V> valueFunc) {
super(emptyMap);
this.keyFunc = keyFunc;
this.valueFunc = valueFunc;
}
// ------------------------------------------------------------------------- Constructor end
/**
* 根据函数自定义键
*
* @param key KEY
* @return 驼峰Key
*/
@Override
protected K customKey(Object key) {
if (null != this.keyFunc) {
return keyFunc.apply(key);
}
//noinspection unchecked
return (K) key;
}
@Override
protected V customValue(Object value) {
if (null != this.valueFunc) {
return valueFunc.apply(value);
}
//noinspection unchecked
return (V) value;
}
}

View File

@ -2,6 +2,9 @@ package cn.hutool.core.map;
import cn.hutool.core.util.ObjectUtil;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
@ -11,6 +14,7 @@ import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Map包装类通过包装一个已有Map实现特定功能例如自定义Key的规则或Value规则
@ -34,6 +38,17 @@ public class MapWrapper<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, S
private Map<K, V> raw;
/**
* 构造<br>
* 通过传入一个Map从而确定Map的类型子类需创建一个空的Map而非传入一个已有Map否则值可能会被修改
*
* @param mapFactory 空Map创建工厂
* @since 5.8.0
*/
public MapWrapper(Supplier<Map<K, V>> mapFactory) {
this(mapFactory.get());
}
/**
* 构造
*
@ -199,11 +214,23 @@ public class MapWrapper<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, S
@Override
public MapWrapper<K, V> clone() throws CloneNotSupportedException {
@SuppressWarnings("unchecked")
final MapWrapper<K, V> clone = (MapWrapper<K, V>) super.clone();
@SuppressWarnings("unchecked") final MapWrapper<K, V> clone = (MapWrapper<K, V>) super.clone();
clone.raw = ObjectUtil.clone(raw);
return clone;
}
//---------------------------------------------------------------------------- Override default methods end
// region 序列化与反序列化重写
private void writeObject(final ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(this.raw);
}
@SuppressWarnings("unchecked")
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
raw = (Map<K, V>) in.readObject();
}
// endregion
}

View File

@ -0,0 +1,322 @@
package cn.hutool.core.map;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.lang.func.VoidFunc1;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReferenceUtil;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 线程安全的ReferenceMap实现<br>
* 参考jdk.management.resource.internal.WeakKeyConcurrentHashMap
*
* @param <K> 键类型
* @param <V> 值类型
* @author looly
* @since 5.8.0
*/
public class ReferenceConcurrentMap<K, V> implements ConcurrentMap<K, V>, Iterable<Map.Entry<K, V>>, Serializable {
final ConcurrentMap<Reference<K>, V> raw;
private final ReferenceQueue<K> lastQueue;
private final ReferenceUtil.ReferenceType keyType;
/**
* 回收监听
*/
private VoidFunc1<Reference<? extends K>> purgeListener;
// region 构造
/**
* 构造
*
* @param raw {@link ConcurrentMap}实现
* @param referenceType Reference类型
*/
public ReferenceConcurrentMap(ConcurrentMap<Reference<K>, V> raw, ReferenceUtil.ReferenceType referenceType) {
this.raw = raw;
this.keyType = referenceType;
lastQueue = new ReferenceQueue<>();
}
// endregion
/**
* 设置对象回收清除监听
*
* @param purgeListener 监听函数
*/
public void setPurgeListener(VoidFunc1<Reference<? extends K>> purgeListener) {
this.purgeListener = purgeListener;
}
@Override
public int size() {
this.purgeStaleKeys();
return this.raw.size();
}
@Override
public boolean isEmpty() {
return 0 == size();
}
@Override
public V get(Object key) {
this.purgeStaleKeys();
//noinspection unchecked
return this.raw.get(ofKey((K) key, null));
}
@Override
public boolean containsKey(Object key) {
this.purgeStaleKeys();
//noinspection unchecked
return this.raw.containsKey(ofKey((K) key, null));
}
@Override
public boolean containsValue(Object value) {
this.purgeStaleKeys();
return this.raw.containsValue(value);
}
@Override
public V put(K key, V value) {
this.purgeStaleKeys();
return this.raw.put(ofKey(key, this.lastQueue), value);
}
@Override
public V putIfAbsent(K key, V value) {
this.purgeStaleKeys();
return this.raw.putIfAbsent(ofKey(key, this.lastQueue), value);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
m.forEach(this::put);
}
@Override
public V replace(K key, V value) {
this.purgeStaleKeys();
return this.raw.replace(ofKey(key, this.lastQueue), value);
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
this.purgeStaleKeys();
return this.raw.replace(ofKey(key, this.lastQueue), oldValue, newValue);
}
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
this.purgeStaleKeys();
this.raw.replaceAll((kWeakKey, value) -> function.apply(kWeakKey.get(), value));
}
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
this.purgeStaleKeys();
return this.raw.computeIfAbsent(ofKey(key, this.lastQueue), kWeakKey -> mappingFunction.apply(key));
}
@Override
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
this.purgeStaleKeys();
return this.raw.computeIfPresent(ofKey(key, this.lastQueue), (kWeakKey, value) -> remappingFunction.apply(key, value));
}
/**
* 从缓存中获得对象当对象不在缓存中或已经过期返回Func0回调产生的对象
*
* @param key
* @param supplier 如果不存在回调方法用于生产值对象
* @return 值对象
*/
public V computeIfAbsent(K key, Func0<? extends V> supplier) {
return computeIfAbsent(key, (keyParam) -> supplier.callWithRuntimeException());
}
@Override
public V remove(Object key) {
this.purgeStaleKeys();
//noinspection unchecked
return this.raw.remove(ofKey((K) key, null));
}
@Override
public boolean remove(Object key, Object value) {
this.purgeStaleKeys();
//noinspection unchecked
return this.raw.remove(ofKey((K) key, null), value);
}
@Override
public void clear() {
this.raw.clear();
//noinspection StatementWithEmptyBody
while (lastQueue.poll() != null) ;
}
@Override
public Set<K> keySet() {
// TODO 非高效方式的set转换应该返回一个view
final Collection<K> trans = CollUtil.trans(this.raw.keySet(), (reference) -> null == reference ? null : reference.get());
return new HashSet<>(trans);
}
@Override
public Collection<V> values() {
this.purgeStaleKeys();
return this.raw.values();
}
@Override
public Set<Entry<K, V>> entrySet() {
this.purgeStaleKeys();
return this.raw.entrySet().stream()
.map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getKey().get(), entry.getValue()))
.collect(Collectors.toSet());
}
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
this.purgeStaleKeys();
this.raw.forEach((key, value)-> action.accept(key.get(), value));
}
@Override
public Iterator<Entry<K, V>> iterator() {
return entrySet().iterator();
}
@Override
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
this.purgeStaleKeys();
return this.raw.compute(ofKey(key, this.lastQueue), (kWeakKey, value) -> remappingFunction.apply(key, value));
}
@Override
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
this.purgeStaleKeys();
return this.raw.merge(ofKey(key, this.lastQueue), value, remappingFunction);
}
/**
* 清除被回收的键
*/
private void purgeStaleKeys() {
Reference<? extends K> reference;
while ((reference = this.lastQueue.poll()) != null) {
this.raw.remove(reference);
if (null != purgeListener) {
purgeListener.callWithRuntimeException(reference);
}
}
}
/**
* 根据Reference类型构建key对应的{@link Reference}
*
* @param key
* @param queue {@link ReferenceQueue}
* @return {@link Reference}
*/
private Reference<K> ofKey(K key, ReferenceQueue<? super K> queue) {
switch (keyType) {
case WEAK:
return new WeakKey<>(key, queue);
case SOFT:
return new SoftKey<>(key, queue);
}
throw new IllegalArgumentException("Unsupported key type: " + keyType);
}
/**
* 弱键
*
* @param <K> 键类型
*/
private static class WeakKey<K> extends WeakReference<K> {
private final int hashCode;
/**
* 构造
*
* @param key 原始Key不能为{@code null}
* @param queue {@link ReferenceQueue}
*/
WeakKey(K key, ReferenceQueue<? super K> queue) {
super(key, queue);
hashCode = key.hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
} else if (other instanceof WeakKey) {
return ObjectUtil.equals(((WeakKey<?>) other).get(), get());
}
return false;
}
}
/**
* 弱键
*
* @param <K> 键类型
*/
private static class SoftKey<K> extends SoftReference<K> {
private final int hashCode;
/**
* 构造
*
* @param key 原始Key不能为{@code null}
* @param queue {@link ReferenceQueue}
*/
SoftKey(K key, ReferenceQueue<? super K> queue) {
super(key, queue);
hashCode = key.hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
} else if (other instanceof SoftKey) {
return ObjectUtil.equals(((SoftKey<?>) other).get(), get());
}
return false;
}
}
}

View File

@ -0,0 +1,118 @@
package cn.hutool.core.map;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Supplier;
/**
* 自定义键和值转换的的Map<br>
* 继承此类后通过实现{@link #customKey(Object)}{@link #customValue(Object)}按照给定规则加入到map或获取值
*
* @param <K> 键类型
* @param <V> 值类型
* @author Looly
* @since 5.8.0
*/
public abstract class TransMap<K, V> extends MapWrapper<K, V> {
private static final long serialVersionUID = 1L;
/**
* 构造<br>
* 通过传入一个Map从而确定Map的类型子类需创建一个空的Map而非传入一个已有Map否则值可能会被修改
*
* @param mapFactory 空Map创建工厂
* @since 5.8.0
*/
public TransMap(Supplier<Map<K, V>> mapFactory) {
super(mapFactory);
}
/**
* 构造<br>
* 通过传入一个Map从而确定Map的类型子类需创建一个空的Map而非传入一个已有Map否则值可能会被修改
*
* @param emptyMap Map 被包装的Map必须为空Map否则自定义key会无效
* @since 3.1.2
*/
public TransMap(Map<K, V> emptyMap) {
super(emptyMap);
}
@Override
public V get(Object key) {
return super.get(customKey(key));
}
@Override
public V put(K key, V value) {
return super.put(customKey(key), customValue(value));
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
m.forEach(this::put);
}
@Override
public boolean containsKey(Object key) {
return super.containsKey(customKey(key));
}
@Override
public V remove(Object key) {
return super.remove(customKey(key));
}
@Override
public boolean remove(Object key, Object value) {
return super.remove(customKey(key), customValue(value));
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
return super.replace(customKey(key), customValue(oldValue), customValue(values()));
}
@Override
public V replace(K key, V value) {
return super.replace(customKey(key), customValue(value));
}
//---------------------------------------------------------------------------- Override default methods start
@Override
public V getOrDefault(Object key, V defaultValue) {
return super.getOrDefault(customKey(key), customValue(defaultValue));
}
@Override
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
return super.computeIfPresent(customKey(key), (k, v) -> remappingFunction.apply(customKey(k), customValue(v)));
}
@Override
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
return super.compute(customKey(key), (k, v) -> remappingFunction.apply(customKey(k), customValue(v)));
}
@Override
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
return super.merge(customKey(key), customValue(value), (v1, v2) -> remappingFunction.apply(customValue(v1), customValue(v2)));
}
//---------------------------------------------------------------------------- Override default methods end
/**
* 自定义键
*
* @param key KEY
* @return 自定义KEY
*/
protected abstract K customKey(Object key);
/**
* 自定义值
*
* @param value
* @return 自定义值
*/
protected abstract V customValue(Object value);
}

View File

@ -0,0 +1,35 @@
package cn.hutool.core.map;
import cn.hutool.core.util.ReferenceUtil;
import java.lang.ref.Reference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 线程安全的WeakMap实现<br>
* 参考jdk.management.resource.internal.WeakKeyConcurrentHashMap
*
* @param <K> 键类型
* @param <V> 值类型
* @author looly
* @since 5.8.0
*/
public class WeakConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
/**
* 构造
*/
public WeakConcurrentMap() {
this(new ConcurrentHashMap<>());
}
/**
* 构造
*
* @param raw {@link ConcurrentMap}实现
*/
public WeakConcurrentMap(ConcurrentMap<Reference<K>, V> raw) {
super(raw, ReferenceUtil.ReferenceType.WEAK);
}
}

View File

@ -12,6 +12,8 @@ import java.util.concurrent.locks.Lock;
*/
public class NoLock implements Lock{
public static NoLock INSTANCE = new NoLock();
@Override
public void lock() {
}

View File

@ -0,0 +1,22 @@
package cn.hutool.core.thread.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
/**
* 无锁的读写锁实现
*
* @author looly
* @since 5.8.0
*/
public class NoReadWriteLock implements ReadWriteLock {
@Override
public Lock readLock() {
return NoLock.INSTANCE;
}
@Override
public Lock writeLock() {
return NoLock.INSTANCE;
}
}

View File

@ -4,7 +4,8 @@ import cn.hutool.core.convert.BasicType;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.JarClassLoader;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.text.CharPool;
import java.io.File;
@ -49,7 +50,7 @@ public class ClassLoaderUtil {
* 原始类型名和其class对应表例如int = int.class
*/
private static final Map<String, Class<?>> PRIMITIVE_TYPE_NAME_MAP = new ConcurrentHashMap<>(32);
private static final SimpleCache<String, Class<?>> CLASS_CACHE = new SimpleCache<>();
private static final WeakConcurrentMap<Pair<String, ClassLoader>, Class<?>> CLASS_CACHE = new WeakConcurrentMap<>();
static {
List<Class<?>> primitiveTypes = new ArrayList<>(32);
@ -179,7 +180,7 @@ public class ClassLoaderUtil {
* </pre>
*
* @param name 类名
* @param classLoader {@link ClassLoader}{@code null} 则使用系统默认ClassLoader
* @param classLoader {@link ClassLoader}{@code null} 则使用{@link #getClassLoader()}获取
* @param isInitialized 是否初始化类调用static模块内容和初始化static属性
* @return 类名对应的类
* @throws UtilException 包装{@link ClassNotFoundException}没有类名对应的类时抛出此异常
@ -189,49 +190,18 @@ public class ClassLoaderUtil {
// 自动将包名中的"/"替换为"."
name = name.replace(CharPool.SLASH, CharPool.DOT);
if(null == classLoader){
classLoader = getClassLoader();
}
// 加载原始类型和缓存中的类
Class<?> clazz = loadPrimitiveClass(name);
if (clazz == null) {
clazz = CLASS_CACHE.get(name);
final String finalName = name;
final ClassLoader finalClassLoader = classLoader;
clazz = CLASS_CACHE.computeIfAbsent(Pair.of(name, classLoader), (key)-> doLoadClass(finalName, finalClassLoader, isInitialized));
}
if (clazz != null) {
return clazz;
}
if (name.endsWith(ARRAY_SUFFIX)) {
// 对象数组"java.lang.String[]"风格
final String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
final Class<?> elementClass = loadClass(elementClassName, classLoader, isInitialized);
clazz = Array.newInstance(elementClass, 0).getClass();
} else if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
// "[Ljava.lang.String;" 风格
final String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
final Class<?> elementClass = loadClass(elementName, classLoader, isInitialized);
clazz = Array.newInstance(elementClass, 0).getClass();
} else if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
// "[[I" "[[Ljava.lang.String;" 风格
final String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
final Class<?> elementClass = loadClass(elementName, classLoader, isInitialized);
clazz = Array.newInstance(elementClass, 0).getClass();
} else {
// 加载普通类
if (null == classLoader) {
classLoader = getClassLoader();
}
try {
clazz = Class.forName(name, isInitialized, classLoader);
} catch (ClassNotFoundException ex) {
// 尝试获取内部类例如java.lang.Thread.State =java.lang.Thread$State
clazz = tryLoadInnerClass(name, classLoader, isInitialized);
if (null == clazz) {
throw new UtilException(ex);
}
}
}
// 加入缓存并返回
return CLASS_CACHE.put(name, clazz);
return clazz;
}
/**
@ -311,6 +281,47 @@ public class ClassLoaderUtil {
}
// ----------------------------------------------------------------------------------- Private method start
/**
* 加载非原始类类无缓存
* @param name 类名
* @param classLoader {@link ClassLoader}
* @param isInitialized 是否初始化
* @return
*/
private static Class<?> doLoadClass(String name, ClassLoader classLoader, boolean isInitialized){
Class<?> clazz;
if (name.endsWith(ARRAY_SUFFIX)) {
// 对象数组"java.lang.String[]"风格
final String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
final Class<?> elementClass = loadClass(elementClassName, classLoader, isInitialized);
clazz = Array.newInstance(elementClass, 0).getClass();
} else if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
// "[Ljava.lang.String;" 风格
final String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
final Class<?> elementClass = loadClass(elementName, classLoader, isInitialized);
clazz = Array.newInstance(elementClass, 0).getClass();
} else if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
// "[[I" "[[Ljava.lang.String;" 风格
final String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
final Class<?> elementClass = loadClass(elementName, classLoader, isInitialized);
clazz = Array.newInstance(elementClass, 0).getClass();
} else {
// 加载普通类
if (null == classLoader) {
classLoader = getClassLoader();
}
try {
clazz = Class.forName(name, isInitialized, classLoader);
} catch (ClassNotFoundException ex) {
// 尝试获取内部类例如java.lang.Thread.State =java.lang.Thread$State
clazz = tryLoadInnerClass(name, classLoader, isInitialized);
if (null == clazz) {
throw new UtilException(ex);
}
}
}
return clazz;
}
/**
* 尝试转换并加载内部类例如java.lang.Thread.State =java.lang.Thread$State

View File

@ -8,9 +8,9 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.lang.reflect.MethodHandleUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.WeakConcurrentMap;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
@ -36,15 +36,15 @@ public class ReflectUtil {
/**
* 构造对象缓存
*/
private static final SimpleCache<Class<?>, Constructor<?>[]> CONSTRUCTORS_CACHE = new SimpleCache<>();
private static final WeakConcurrentMap<Class<?>, Constructor<?>[]> CONSTRUCTORS_CACHE = new WeakConcurrentMap<>();
/**
* 字段缓存
*/
private static final SimpleCache<Class<?>, Field[]> FIELDS_CACHE = new SimpleCache<>();
private static final WeakConcurrentMap<Class<?>, Field[]> FIELDS_CACHE = new WeakConcurrentMap<>();
/**
* 方法缓存
*/
private static final SimpleCache<Class<?>, Method[]> METHODS_CACHE = new SimpleCache<>();
private static final WeakConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new WeakConcurrentMap<>();
// --------------------------------------------------------------------------------------------------------- Constructor
@ -86,7 +86,7 @@ public class ReflectUtil {
@SuppressWarnings("unchecked")
public static <T> Constructor<T>[] getConstructors(Class<T> beanClass) throws SecurityException {
Assert.notNull(beanClass);
return (Constructor<T>[]) CONSTRUCTORS_CACHE.get(beanClass, () -> getConstructorsDirectly(beanClass));
return (Constructor<T>[]) CONSTRUCTORS_CACHE.computeIfAbsent(beanClass, () -> getConstructorsDirectly(beanClass));
}
/**
@ -175,7 +175,7 @@ public class ReflectUtil {
*/
public static Field[] getFields(Class<?> beanClass) throws SecurityException {
Assert.notNull(beanClass);
return FIELDS_CACHE.get(beanClass, () -> getFieldsDirectly(beanClass, true));
return FIELDS_CACHE.computeIfAbsent(beanClass, () -> getFieldsDirectly(beanClass, true));
}
@ -651,7 +651,7 @@ public class ReflectUtil {
*/
public static Method[] getMethods(Class<?> beanClass) throws SecurityException {
Assert.notNull(beanClass);
return METHODS_CACHE.get(beanClass,
return METHODS_CACHE.computeIfAbsent(beanClass,
() -> getMethodsDirectly(beanClass, true, true));
}

View File

@ -50,7 +50,7 @@ public class SimpleCacheTest {
final SimpleCache<String, String> cache = new SimpleCache<>();
final ConcurrencyTester tester = new ConcurrencyTester(9000);
tester.test(()-> cache.get("aaa", ()-> {
ThreadUtil.sleep(1000);
ThreadUtil.sleep(200);
return "aaaValue";
}));

View File

@ -0,0 +1,22 @@
package cn.hutool.core.map;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
public class FuncMapTest {
@Test
public void putGetTest(){
final FuncMap<Object, Object> map = new FuncMap<>(HashMap::new,
(key)->key.toString().toLowerCase(),
(value)->value.toString().toUpperCase());
map.put("aaa", "b");
map.put("BBB", "c");
Assert.assertEquals("B", map.get("aaa"));
Assert.assertEquals("C", map.get("bbb"));
}
}

View File

@ -0,0 +1,52 @@
package cn.hutool.core.map;
import cn.hutool.core.thread.ConcurrencyTester;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import org.junit.Assert;
import org.junit.Test;
public class WeakConcurrentMapTest {
@Test
public void putAndGetTest(){
final WeakConcurrentMap<Object, Object> map = new WeakConcurrentMap<>();
Object
key1 = new Object(), value1 = new Object(),
key2 = new Object(), value2 = new Object(),
key3 = new Object(), value3 = new Object(),
key4 = new Object(), value4 = new Object();
map.put(key1, value1);
map.put(key2, value2);
map.put(key3, value3);
map.put(key4, value4);
Assert.assertEquals(value1, map.get(key1));
Assert.assertEquals(value2, map.get(key2));
Assert.assertEquals(value3, map.get(key3));
Assert.assertEquals(value4, map.get(key4));
// 清空引用
//noinspection UnusedAssignment
key1 = null;
//noinspection UnusedAssignment
key2 = null;
System.gc();
ThreadUtil.sleep(200L);
Assert.assertEquals(2, map.size());
}
@Test
public void getConcurrencyTest(){
final WeakConcurrentMap<String, String> cache = new WeakConcurrentMap<>();
final ConcurrencyTester tester = new ConcurrencyTester(9000);
tester.test(()-> cache.computeIfAbsent("aaa" + RandomUtil.randomInt(2), (key)-> "aaaValue"));
Assert.assertTrue(tester.getInterval() > 0);
String value = ObjectUtil.defaultIfNull(cache.get("aaa0"), cache.get("aaa1"));
Assert.assertEquals("aaaValue", value);
}
}

View File

@ -1,6 +1,6 @@
package cn.hutool.extra.cglib;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.util.StrUtil;
import net.sf.cglib.beans.BeanCopier;
import net.sf.cglib.core.Converter;
@ -18,7 +18,7 @@ public enum BeanCopierCache {
*/
INSTANCE;
private final SimpleCache<String, BeanCopier> cache = new SimpleCache<>();
private final WeakConcurrentMap<String, BeanCopier> cache = new WeakConcurrentMap<>();
/**
* 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素
@ -43,7 +43,7 @@ public enum BeanCopierCache {
*/
public BeanCopier get(Class<?> srcClass, Class<?> targetClass, boolean useConverter) {
final String key = genKey(srcClass, targetClass, useConverter);
return cache.get(key, () -> BeanCopier.create(srcClass, targetClass, useConverter));
return cache.computeIfAbsent(key, () -> BeanCopier.create(srcClass, targetClass, useConverter));
}
/**

View File

@ -1,6 +1,6 @@
package cn.hutool.script;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.util.StrUtil;
import javax.script.Bindings;
@ -20,7 +20,7 @@ import javax.script.ScriptException;
public class ScriptUtil {
private static final ScriptEngineManager MANAGER = new ScriptEngineManager();
private static final SimpleCache<String, ScriptEngine> CACHE = new SimpleCache<>();
private static final WeakConcurrentMap<String, ScriptEngine> CACHE = new WeakConcurrentMap<>();
/**
* 获得单例的{@link ScriptEngine} 实例
@ -29,7 +29,7 @@ public class ScriptUtil {
* @return {@link ScriptEngine} 实例
*/
public static ScriptEngine getScript(String nameOrExtOrMime) {
return CACHE.get(nameOrExtOrMime, () -> createScript(nameOrExtOrMime));
return CACHE.computeIfAbsent(nameOrExtOrMime, () -> createScript(nameOrExtOrMime));
}
/**