mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-12-06 17:18:54 +08:00
修复ReflectUtil中因class和Method关联导致的缓存无法回收问题(issue#4039@Github)
This commit is contained in:
parent
c1c92def35
commit
bd123386ce
@ -2,10 +2,16 @@
|
||||
# 🚀Changelog
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
# 5.8.41(2025-08-26)
|
||||
# 5.8.41(2025-08-29)
|
||||
|
||||
### 🐣新特性
|
||||
* 【core 】 增加`WeakKeyValueConcurrentMap`及其关联类,同时废弃`WeakConcurrentMap`并替换(issue#4039@Github)
|
||||
* 【core 】 `MapUtil`增加`removeByValue`和`removeIf`方法
|
||||
* 【core 】 `ObjectUtil`增加`apply`方法
|
||||
* 【core 】 `ReferenceUtil`增加`get`方法
|
||||
|
||||
### 🐞Bug修复
|
||||
* 【core 】 修复`ReflectUtil`中因class和Method关联导致的缓存无法回收问题(issue#4039@Github)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
# 5.8.40(2025-08-26)
|
||||
|
||||
@ -3,9 +3,8 @@ package cn.hutool.cache.impl;
|
||||
import cn.hutool.cache.CacheListener;
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.lang.mutable.Mutable;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import cn.hutool.core.lang.ref.Ref;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
|
||||
/**
|
||||
* 弱引用缓存<br>
|
||||
@ -27,16 +26,18 @@ public class WeakCache<K, V> extends TimedCache<K, V>{
|
||||
* @param timeout 超时时常,单位毫秒,-1或0表示无限制
|
||||
*/
|
||||
public WeakCache(long timeout) {
|
||||
super(timeout, new WeakConcurrentMap<>());
|
||||
super(timeout, new WeakKeyValueConcurrentMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeakCache<K, V> setListener(CacheListener<K, V> listener) {
|
||||
super.setListener(listener);
|
||||
|
||||
final WeakConcurrentMap<Mutable<K>, CacheObj<K, V>> map = (WeakConcurrentMap<Mutable<K>, CacheObj<K, V>>) this.cacheMap;
|
||||
final WeakKeyValueConcurrentMap<Mutable<K>, CacheObj<K, V>> map = (WeakKeyValueConcurrentMap<Mutable<K>, CacheObj<K, V>>) this.cacheMap;
|
||||
// WeakKey回收之后,key对应的值已经是null了,因此此处的key也为null
|
||||
map.setPurgeListener((key, value)-> listener.onRemove(Opt.ofNullable(key).map(Reference::get).map(Mutable::get).get(), value.getValue()));
|
||||
map.setPurgeListener((key, value)-> listener.onRemove(
|
||||
Opt.ofNullable(key).map(Ref::get).map(Mutable::get).get(),
|
||||
Opt.ofNullable(value).map(Ref::get).map(CacheObj::getValue).get()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package cn.hutool.core.bean;
|
||||
|
||||
import cn.hutool.core.lang.func.Func0;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
|
||||
/**
|
||||
* Bean属性缓存<br>
|
||||
@ -12,7 +12,7 @@ import cn.hutool.core.map.WeakConcurrentMap;
|
||||
public enum BeanDescCache {
|
||||
INSTANCE;
|
||||
|
||||
private final WeakConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakConcurrentMap<>();
|
||||
private final WeakKeyValueConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获得属性名和{@link BeanDesc}Map映射
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package cn.hutool.core.bean;
|
||||
|
||||
import cn.hutool.core.lang.func.Func0;
|
||||
import cn.hutool.core.map.ReferenceConcurrentMap;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.ReferenceConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.Map;
|
||||
@ -16,8 +16,8 @@ import java.util.Map;
|
||||
public enum BeanInfoCache {
|
||||
INSTANCE;
|
||||
|
||||
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> pdCache = new WeakConcurrentMap<>();
|
||||
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new WeakConcurrentMap<>();
|
||||
private final WeakKeyValueConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> pdCache = new WeakKeyValueConcurrentMap<>();
|
||||
private final WeakKeyValueConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获得属性名和{@link PropertyDescriptor}Map映射
|
||||
|
||||
@ -4,7 +4,7 @@ import cn.hutool.core.convert.AbstractConverter;
|
||||
import cn.hutool.core.convert.ConvertException;
|
||||
import cn.hutool.core.lang.EnumItem;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
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 WeakConcurrentMap<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
private final Class enumClass;
|
||||
|
||||
|
||||
@ -204,7 +204,6 @@ public class ColorUtil {
|
||||
*
|
||||
* @param color 指定颜色
|
||||
* @return 其余颜色与color的最大距离
|
||||
* @since 6.0.0-M16
|
||||
*/
|
||||
public static int maxDistance(final Color color) {
|
||||
if (null == color) {
|
||||
|
||||
@ -735,7 +735,6 @@ public class PathUtil {
|
||||
* @param dir 临时文件创建的所在目录
|
||||
* @return 临时文件
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static Path createTempFile(final String prefix, final String suffix, final Path dir) throws IORuntimeException {
|
||||
int exceptionsCount = 0;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package cn.hutool.core.lang;
|
||||
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -196,7 +196,7 @@ public class PatternPool {
|
||||
/**
|
||||
* Pattern池
|
||||
*/
|
||||
private static final WeakConcurrentMap<RegexWithFlag, Pattern> POOL = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<RegexWithFlag, Pattern> POOL = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 先从Pattern池中查找正则对应的{@link Pattern},找不到则编译正则表达式并入池。
|
||||
|
||||
@ -6,7 +6,7 @@ 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.SafeConcurrentHashMap;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
@ -19,7 +19,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 简单缓存,无超时实现,默认使用{@link WeakConcurrentMap}实现缓存自动清理
|
||||
* 简单缓存,无超时实现,默认使用{@link WeakKeyValueConcurrentMap}实现缓存自动清理
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
@ -43,7 +43,7 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
|
||||
* 构造,默认使用{@link WeakHashMap}实现缓存自动清理
|
||||
*/
|
||||
public SimpleCache() {
|
||||
this(new WeakConcurrentMap<>());
|
||||
this(new WeakKeyValueConcurrentMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package cn.hutool.core.lang.func;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
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 WeakConcurrentMap<String, SerializedLambda> cache = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<String, SerializedLambda> cache = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 通过对象的方法或类的静态方法引用,获取lambda实现类
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package cn.hutool.core.lang.intern;
|
||||
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
@ -12,7 +12,7 @@ import java.lang.ref.WeakReference;
|
||||
*/
|
||||
public class WeakInterner<T> implements Interner<T>{
|
||||
|
||||
private final WeakConcurrentMap<T, WeakReference<T>> cache = new WeakConcurrentMap<>();
|
||||
private final WeakKeyValueConcurrentMap<T, WeakReference<T>> cache = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
public T intern(T sample) {
|
||||
if (sample == null) {
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
package cn.hutool.core.lang.ref;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 虚引用对象,在GC时发现虚引用对象,会将{@link PhantomReference}插入{@link ReferenceQueue}。 <br>
|
||||
* 此时对象未被真正回收,要等到{@link ReferenceQueue}被真正处理后才会被回收。
|
||||
*
|
||||
* @param <T> 键类型
|
||||
*/
|
||||
public class PhantomObj<T> extends PhantomReference<T> implements Ref<T>{
|
||||
private final int hashCode;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param obj 原始对象
|
||||
* @param queue {@link ReferenceQueue}
|
||||
*/
|
||||
public PhantomObj(final T obj, final ReferenceQueue<? super T> queue) {
|
||||
super(obj, queue);
|
||||
hashCode = Objects.hashCode(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
} else if (other instanceof PhantomObj) {
|
||||
return ObjUtil.equals(((PhantomObj<?>) other).get(), get());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
18
hutool-core/src/main/java/cn/hutool/core/lang/ref/Ref.java
Normal file
18
hutool-core/src/main/java/cn/hutool/core/lang/ref/Ref.java
Normal file
@ -0,0 +1,18 @@
|
||||
package cn.hutool.core.lang.ref;
|
||||
|
||||
/**
|
||||
* 针对{@link java.lang.ref.Reference}的接口定义,用于扩展功能<br>
|
||||
* 例如提供自定义的无需回收对象
|
||||
*
|
||||
* @param <T> 对象类型
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Ref<T> {
|
||||
|
||||
/**
|
||||
* 获取引用的原始对象
|
||||
*
|
||||
* @return 原始对象
|
||||
*/
|
||||
T get();
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package cn.hutool.core.lang.ref;
|
||||
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
|
||||
/**
|
||||
* 引用类型
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public enum ReferenceType {
|
||||
/**
|
||||
* 强引用,不回收
|
||||
*/
|
||||
STRONG,
|
||||
/**
|
||||
* 软引用,在GC报告内存不足时会被GC回收
|
||||
*/
|
||||
SOFT,
|
||||
/**
|
||||
* 弱引用,在GC时发现弱引用会回收其对象
|
||||
*/
|
||||
WEAK,
|
||||
/**
|
||||
* 虚引用,在GC时发现虚引用对象,会将{@link PhantomReference}插入{@link ReferenceQueue}。 <br>
|
||||
* 此时对象未被真正回收,要等到{@link ReferenceQueue}被真正处理后才会被回收。
|
||||
*/
|
||||
PHANTOM
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package cn.hutool.core.lang.ref;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 软引用对象,在GC报告内存不足时会被GC回收
|
||||
*
|
||||
* @param <T> 键类型
|
||||
*/
|
||||
public class SoftObj<T> extends SoftReference<T> implements Ref<T>{
|
||||
private final int hashCode;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param obj 原始对象
|
||||
* @param queue {@link ReferenceQueue}
|
||||
*/
|
||||
public SoftObj(final T obj, final ReferenceQueue<? super T> queue) {
|
||||
super(obj, queue);
|
||||
hashCode = Objects.hashCode(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
} else if (other instanceof SoftObj) {
|
||||
return Objects.equals(((SoftObj<?>) other).get(), get());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package cn.hutool.core.lang.ref;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 弱引用对象,在GC时发现弱引用会回收其对象
|
||||
*
|
||||
* @param <T> 键类型
|
||||
*/
|
||||
public class StrongObj<T> implements Ref<T> {
|
||||
|
||||
private final T obj;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param obj 原始对象
|
||||
*/
|
||||
public StrongObj(final T obj) {
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return this.obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
} else if (other instanceof StrongObj) {
|
||||
return Objects.equals(((StrongObj<?>) other).get(), get());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package cn.hutool.core.lang.ref;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 弱引用对象,在GC时发现弱引用会回收其对象
|
||||
*
|
||||
* @param <T> 键类型
|
||||
*/
|
||||
public class WeakObj<T> extends WeakReference<T> implements Ref<T>{
|
||||
private final int hashCode;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param obj 原始对象
|
||||
* @param queue {@link ReferenceQueue}
|
||||
*/
|
||||
public WeakObj(final T obj, final ReferenceQueue<? super T> queue) {
|
||||
super(obj, queue);
|
||||
hashCode = Objects.hashCode(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
} else if (other instanceof WeakObj) {
|
||||
return ObjUtil.equals(((WeakObj<?>) other).get(), get());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 引用工具封装,主要针对{@link java.lang.ref.Reference} 工具化封装<br>
|
||||
* 主要封装包括:
|
||||
* <pre>
|
||||
* 1. {@link java.lang.ref.SoftReference} 软引用,在GC报告内存不足时会被GC回收
|
||||
* 2. {@link java.lang.ref.WeakReference} 弱引用,在GC时发现弱引用会回收其对象
|
||||
* 3. {@link java.lang.ref.PhantomReference} 虚引用,在GC时发现虚引用对象,会将{@link java.lang.ref.PhantomReference}插入{@link java.lang.ref.ReferenceQueue}。 此时对象未被真正回收,要等到{@link java.lang.ref.ReferenceQueue}被真正处理后才会被回收。
|
||||
* </pre>
|
||||
*/
|
||||
package cn.hutool.core.lang.ref;
|
||||
@ -1,7 +1,7 @@
|
||||
package cn.hutool.core.lang.reflect;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.TypeUtil;
|
||||
|
||||
@ -20,7 +20,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class ActualTypeMapperPool {
|
||||
|
||||
private static final WeakConcurrentMap<Type, Map<Type, Type>> CACHE = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<Type, Map<Type, Type>> CACHE = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获取泛型变量和泛型实际类型的对应关系Map
|
||||
|
||||
@ -5,16 +5,14 @@ import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.exceptions.UtilException;
|
||||
import cn.hutool.core.lang.*;
|
||||
import cn.hutool.core.stream.CollectorUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.JdkUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Map相关工具类
|
||||
@ -1347,6 +1345,40 @@ public class MapUtil {
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除Map中值为指定值的键值对<br>
|
||||
* 注意:此方法在传入的Map上直接修改。
|
||||
*
|
||||
* @param <K> key的类型
|
||||
* @param <V> value的类型
|
||||
* @param map Map
|
||||
* @param value 给定值
|
||||
* @return map
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public static <K, V> Map<K, V> removeByValue(final Map<K, V> map, final V value) {
|
||||
return removeIf(map, entry -> ObjUtil.equals(value, entry.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除Map中值为{@code null}的键值对<br>
|
||||
* 注意:此方法在传入的Map上直接修改。
|
||||
*
|
||||
* @param <K> key的类型
|
||||
* @param <V> value的类型
|
||||
* @param map Map
|
||||
* @param predicate 移除条件,当{@link Predicate#test(Object)}为{@code true}时移除
|
||||
* @return map
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public static <K, V> Map<K, V> removeIf(final Map<K, V> map, final Predicate<Entry<K, V>> predicate) {
|
||||
if (isEmpty(map)) {
|
||||
return map;
|
||||
}
|
||||
map.entrySet().removeIf(predicate);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个空Map
|
||||
*
|
||||
|
||||
@ -30,6 +30,7 @@ import java.util.stream.Collectors;
|
||||
* @param <V> 值类型
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
* @deprecated 请使用{@link cn.hutool.core.map.reference.ReferenceConcurrentMap}
|
||||
*/
|
||||
public class ReferenceConcurrentMap<K, V> implements ConcurrentMap<K, V>, Iterable<Map.Entry<K, V>>, Serializable {
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ import java.util.concurrent.ConcurrentMap;
|
||||
* @param <V> 值类型
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
* @deprecated 请使用{@link cn.hutool.core.map.reference.WeakKeyConcurrentMap}
|
||||
*/
|
||||
public class WeakConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
|
||||
|
||||
|
||||
@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.core.map.reference;
|
||||
|
||||
import cn.hutool.core.lang.ref.Ref;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ReferenceUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 线程安全的ReferenceMap实现
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @author Looly
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public abstract class ReferenceConcurrentMap<K, V> implements ConcurrentMap<K, V>, Iterable<Map.Entry<K, V>>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 键值对引用
|
||||
*/
|
||||
final ConcurrentMap<Ref<K>, Ref<V>> raw;
|
||||
/**
|
||||
* 因无强链接而清除的键队列
|
||||
*/
|
||||
private final ReferenceQueue<K> lastKeyQueue;
|
||||
/**
|
||||
* 因无强链接而清除的值队列
|
||||
*/
|
||||
private final ReferenceQueue<V> lastValueQueue;
|
||||
/**
|
||||
* 回收监听
|
||||
*/
|
||||
private BiConsumer<Ref<? extends K>, Ref<? extends V>> purgeListener;
|
||||
|
||||
// region 构造
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param raw {@link ConcurrentMap}实现
|
||||
*/
|
||||
public ReferenceConcurrentMap(final ConcurrentMap<Ref<K>, Ref<V>> raw) {
|
||||
this.raw = raw;
|
||||
lastKeyQueue = new ReferenceQueue<>();
|
||||
lastValueQueue = new ReferenceQueue<>();
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 设置对象回收清除监听
|
||||
*
|
||||
* @param purgeListener 监听函数
|
||||
*/
|
||||
public void setPurgeListener(final BiConsumer<Ref<? extends K>, Ref<? extends V>> purgeListener) {
|
||||
this.purgeListener = purgeListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
this.purgeStale();
|
||||
return this.raw.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
this.purgeStale();
|
||||
return this.raw.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(final Object key) {
|
||||
this.purgeStale();
|
||||
return unwrap(this.raw.get(wrapKey(key)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(final Object key) {
|
||||
this.purgeStale();
|
||||
return this.raw.containsKey(wrapKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(final Object value) {
|
||||
this.purgeStale();
|
||||
return this.raw.containsValue(wrapValue(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(final K key, final V value) {
|
||||
this.purgeStale();
|
||||
final Ref<V> vReference = this.raw.put(wrapKey(key), wrapValue(value));
|
||||
return unwrap(vReference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V putIfAbsent(final K key, final V value) {
|
||||
this.purgeStale();
|
||||
final Ref<V> vReference = this.raw.putIfAbsent(wrapKey(key), wrapValue(value));
|
||||
return unwrap(vReference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(final Map<? extends K, ? extends V> m) {
|
||||
m.forEach(this::put);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V replace(final K key, final V value) {
|
||||
this.purgeStale();
|
||||
final Ref<V> vReference = this.raw.replace(wrapKey(key), wrapValue(value));
|
||||
return unwrap(vReference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replace(final K key, final V oldValue, final V newValue) {
|
||||
this.purgeStale();
|
||||
return this.raw.replace(wrapKey(key), wrapValue(oldValue), wrapValue(newValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(final BiFunction<? super K, ? super V, ? extends V> function) {
|
||||
this.purgeStale();
|
||||
this.raw.replaceAll((rKey, rValue) -> wrapValue(function.apply(unwrap(rKey), unwrap(rValue))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V computeIfAbsent(final K key, final Function<? super K, ? extends V> mappingFunction) {
|
||||
V result = null;
|
||||
while(null == result){
|
||||
this.purgeStale();
|
||||
final Ref<V> vReference = this.raw.computeIfAbsent(wrapKey(key),
|
||||
kReference -> wrapValue(mappingFunction.apply(unwrap(kReference))));
|
||||
|
||||
// issue#IA5GMH 如果vReference在此时被GC回收,则unwrap后为null,需要循环计算
|
||||
// 但是当用户提供的值本身为null,则直接返回之
|
||||
if(NullRef.NULL == vReference){
|
||||
// 用户提供的值本身为null
|
||||
return null;
|
||||
}
|
||||
result = unwrap(vReference);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V computeIfPresent(final K key, final BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||
V result = null;
|
||||
while(null == result){
|
||||
this.purgeStale();
|
||||
final Ref<V> vReference = this.raw.computeIfPresent(wrapKey(key),
|
||||
(kReference, vReference1) -> wrapValue(remappingFunction.apply(unwrap(kReference), unwrap(vReference1))));
|
||||
|
||||
// issue#IA5GMH 如果vReference在此时被GC回收,则unwrap后为null,需要循环计算
|
||||
// 但是当用户提供的值本身为null,则直接返回之
|
||||
if(NullRef.NULL == vReference){
|
||||
// 用户提供的值本身为null
|
||||
return null;
|
||||
}
|
||||
result = unwrap(vReference);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(final Object key) {
|
||||
this.purgeStale();
|
||||
return unwrap(this.raw.remove(wrapKey(key)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean remove(final Object key, final Object value) {
|
||||
this.purgeStale();
|
||||
return this.raw.remove(wrapKey((K) key, null), value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
@Override
|
||||
public void clear() {
|
||||
this.raw.clear();
|
||||
while (lastKeyQueue.poll() != null) ;
|
||||
while (lastValueQueue.poll() != null) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
this.purgeStale();
|
||||
final Set<Ref<K>> referenceSet = this.raw.keySet();
|
||||
return new AbstractSet<K>() {
|
||||
@Override
|
||||
public Iterator<K> iterator() {
|
||||
final Iterator<Ref<K>> referenceIter = referenceSet.iterator();
|
||||
return new Iterator<K>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return referenceIter.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K next() {
|
||||
return unwrap(referenceIter.next());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return referenceSet.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
this.purgeStale();
|
||||
final Collection<Ref<V>> referenceValues = this.raw.values();
|
||||
return new AbstractCollection<V>() {
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
final Iterator<Ref<V>> referenceIter = referenceValues.iterator();
|
||||
return new Iterator<V>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return referenceIter.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V next() {
|
||||
return unwrap(referenceIter.next());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return referenceValues.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
this.purgeStale();
|
||||
final Set<Entry<Ref<K>, Ref<V>>> referenceEntrySet = this.raw.entrySet();
|
||||
return new AbstractSet<Entry<K, V>>() {
|
||||
@Override
|
||||
public Iterator<Entry<K, V>> iterator() {
|
||||
final Iterator<Entry<Ref<K>, Ref<V>>> referenceIter = referenceEntrySet.iterator();
|
||||
return new Iterator<Entry<K, V>>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return referenceIter.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> next() {
|
||||
final Entry<Ref<K>, Ref<V>> next = referenceIter.next();
|
||||
return new Entry<K, V>() {
|
||||
@Override
|
||||
public K getKey() {
|
||||
return unwrap(next.getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return unwrap(next.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(final V value) {
|
||||
return unwrap(next.setValue(wrapValue(value)));
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return referenceEntrySet.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(final BiConsumer<? super K, ? super V> action) {
|
||||
this.purgeStale();
|
||||
this.raw.forEach((key, rValue) -> action.accept(key.get(), unwrap(rValue)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<K, V>> iterator() {
|
||||
return entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V compute(final K key, final BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||
this.purgeStale();
|
||||
return unwrap(this.raw.compute(wrapKey(key),
|
||||
(kReference, vReference) -> wrapValue(remappingFunction.apply(unwrap(kReference), unwrap(vReference)))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V merge(final K key, final V value, final BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
|
||||
this.purgeStale();
|
||||
return unwrap(this.raw.merge(wrapKey(key), wrapValue(value),
|
||||
(vReference, vReference2) -> wrapValue(remappingFunction.apply(unwrap(vReference), unwrap(vReference2)))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除被回收的键和值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void purgeStale() {
|
||||
Ref<? extends K> key;
|
||||
Ref<? extends V> value;
|
||||
|
||||
// 清除无效key对应键值对
|
||||
while ((key = (Ref<? extends K>) this.lastKeyQueue.poll()) != null) {
|
||||
value = this.raw.remove(key);
|
||||
if (null != purgeListener) {
|
||||
purgeListener.accept(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
// 清除无效value对应的键值对
|
||||
while ((value = (Ref<? extends V>) this.lastValueQueue.poll()) != null) {
|
||||
MapUtil.removeByValue(this.raw, (Ref<V>) value);
|
||||
if (null != purgeListener) {
|
||||
purgeListener.accept(null, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Reference类型构建key对应的{@link Reference}
|
||||
*
|
||||
* @param key 键
|
||||
* @param queue {@link ReferenceQueue}
|
||||
* @return {@link Reference}
|
||||
*/
|
||||
abstract Ref<K> wrapKey(final K key, final ReferenceQueue<? super K> queue);
|
||||
|
||||
/**
|
||||
* 根据Reference类型构建value对应的{@link Reference}
|
||||
*
|
||||
* @param value 值
|
||||
* @param queue {@link ReferenceQueue}
|
||||
* @return {@link Reference}
|
||||
*/
|
||||
abstract Ref<V> wrapValue(final V value, final ReferenceQueue<? super V> queue);
|
||||
|
||||
/**
|
||||
* 根据Reference类型构建key对应的{@link Reference}
|
||||
*
|
||||
* @param key 键
|
||||
* @return {@link Reference}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Ref<K> wrapKey(final Object key) {
|
||||
return wrapKey((K) key, this.lastKeyQueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Reference类型构建value对应的{@link Reference}
|
||||
*
|
||||
* @param value 键
|
||||
* @return {@link Reference}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Ref<V> wrapValue(final Object value) {
|
||||
if(null == value){
|
||||
return (Ref<V>) NullRef.NULL;
|
||||
}
|
||||
return wrapValue((V) value, this.lastValueQueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 去包装对象
|
||||
*
|
||||
* @param <T> 对象类型
|
||||
* @param obj 对象
|
||||
* @return 值
|
||||
*/
|
||||
private static <T> T unwrap(final Ref<T> obj) {
|
||||
return ReferenceUtil.get(obj);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static class NullRef implements Ref {
|
||||
public static final Object NULL = new NullRef();
|
||||
|
||||
@Override
|
||||
public Object get() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.core.map.reference;
|
||||
|
||||
import cn.hutool.core.lang.ref.Ref;
|
||||
import cn.hutool.core.lang.ref.SoftObj;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* 线程安全的SoftMap实现<br>
|
||||
* 键和值都为Soft引用,即,在GC报告内存不足时会被GC回收
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @author Looly
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public class SoftConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public SoftConcurrentMap() {
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param raw {@link ConcurrentMap}实现
|
||||
*/
|
||||
public SoftConcurrentMap(final ConcurrentMap<Ref<K>, Ref<V>> raw) {
|
||||
super(raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
Ref<K> wrapKey(final K key, final ReferenceQueue<? super K> queue) {
|
||||
return new SoftObj<>(key, queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
Ref<V> wrapValue(final V value, final ReferenceQueue<? super V> queue) {
|
||||
return new SoftObj<>(value, queue);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.core.map.reference;
|
||||
|
||||
import cn.hutool.core.lang.ref.Ref;
|
||||
import cn.hutool.core.lang.ref.StrongObj;
|
||||
import cn.hutool.core.lang.ref.WeakObj;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* 线程安全的WeakMap实现<br>
|
||||
* 键为Weak引用,即,在GC时发现弱引用会回收其对象
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @author Looly
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public class WeakKeyConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public WeakKeyConcurrentMap() {
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param raw {@link ConcurrentMap}实现
|
||||
*/
|
||||
public WeakKeyConcurrentMap(final ConcurrentMap<Ref<K>, Ref<V>> raw) {
|
||||
super(raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
Ref<K> wrapKey(final K key, final ReferenceQueue<? super K> queue) {
|
||||
return new WeakObj<>(key, queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
Ref<V> wrapValue(final V value, final ReferenceQueue<? super V> queue) {
|
||||
return new StrongObj<>(value);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.core.map.reference;
|
||||
|
||||
import cn.hutool.core.lang.ref.Ref;
|
||||
import cn.hutool.core.lang.ref.WeakObj;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* 线程安全的WeakMap实现<br>
|
||||
* 键和值都为Weak引用,即,在GC时发现弱引用会回收其对象
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @author Looly
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public class WeakKeyValueConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public WeakKeyValueConcurrentMap() {
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param raw {@link ConcurrentMap}实现
|
||||
*/
|
||||
public WeakKeyValueConcurrentMap(final ConcurrentMap<Ref<K>, Ref<V>> raw) {
|
||||
super(raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
Ref<K> wrapKey(final K key, final ReferenceQueue<? super K> queue) {
|
||||
return new WeakObj<>(key, queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
Ref<V> wrapValue(final V value, final ReferenceQueue<? super V> queue) {
|
||||
return new WeakObj<>(value, queue);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 弱引用Map实现
|
||||
*
|
||||
* @author Looly
|
||||
* @since 5.8.41
|
||||
*/
|
||||
package cn.hutool.core.map.reference;
|
||||
@ -71,8 +71,6 @@ public class RFC3986 {
|
||||
|
||||
/**
|
||||
* query中的value编码器,严格模式,value中不能包含任何分隔符。
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static final PercentCodec QUERY_PARAM_VALUE_STRICT = UNRESERVED;
|
||||
|
||||
@ -84,8 +82,6 @@ public class RFC3986 {
|
||||
|
||||
/**
|
||||
* query中的key编码器,严格模式,key中不能包含任何分隔符。
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static final PercentCodec QUERY_PARAM_NAME_STRICT = UNRESERVED;
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import cn.hutool.core.map.MapUtil;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@ -367,6 +368,33 @@ public class ObjectUtil {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果指定的对象不为 {@code null},则应用提供的映射函数并返回结果,否则返回 {@code null}。
|
||||
*
|
||||
* @param source 要检查的对象
|
||||
* @param handler 要应用的映射函数
|
||||
* @param <T> 输入对象的类型
|
||||
* @param <R> 映射函数的返回类型
|
||||
* @return 映射函数的结果, 如果输入对象为 null,则返回 null
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public static <T, R> R apply(final T source, final Function<T, R> handler) {
|
||||
return defaultIfNull(source, handler, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果指定的对象不为 {@code null},则执行{@link Consumer}处理source,否则不进行操作
|
||||
*
|
||||
* @param source 要检查的对象
|
||||
* @param consumer source处理逻辑
|
||||
* @param <T> 输入对象的类型
|
||||
*/
|
||||
public static <T> void accept(final T source, final Consumer<T> consumer) {
|
||||
if (null != source) {
|
||||
consumer.accept(source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定对象为{@code null}或者""返回默认值, 否则返回自定义handle处理后的返回值
|
||||
*
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
package cn.hutool.core.util;
|
||||
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import cn.hutool.core.lang.ref.Ref;
|
||||
|
||||
import java.lang.ref.*;
|
||||
|
||||
/**
|
||||
* 引用工具类,主要针对{@link Reference} 工具化封装<br>
|
||||
@ -54,6 +52,30 @@ public class ReferenceUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code null}全的解包获取原始对象
|
||||
*
|
||||
* @param <T> 对象类型
|
||||
* @param obj Reference对象
|
||||
* @return 原始对象 or {@code null}
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public static <T> T get(final Reference<T> obj) {
|
||||
return ObjUtil.apply(obj, Reference::get);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code null}安全的解包获取原始对象
|
||||
*
|
||||
* @param <T> 对象类型
|
||||
* @param obj Ref对象
|
||||
* @return 原始对象 or {@code null}
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public static <T> T get(final Ref<T> obj) {
|
||||
return ObjUtil.apply(obj, Ref::get);
|
||||
}
|
||||
|
||||
/**
|
||||
* 引用类型
|
||||
*
|
||||
|
||||
@ -11,22 +11,10 @@ import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Filter;
|
||||
import cn.hutool.core.lang.reflect.MethodHandleUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 反射工具类
|
||||
@ -39,15 +27,15 @@ public class ReflectUtil {
|
||||
/**
|
||||
* 构造对象缓存
|
||||
*/
|
||||
private static final WeakConcurrentMap<Class<?>, Constructor<?>[]> CONSTRUCTORS_CACHE = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<Class<?>, Constructor<?>[]> CONSTRUCTORS_CACHE = new WeakKeyValueConcurrentMap<>();
|
||||
/**
|
||||
* 字段缓存
|
||||
*/
|
||||
private static final WeakConcurrentMap<Class<?>, Field[]> FIELDS_CACHE = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<Class<?>, Field[]> FIELDS_CACHE = new WeakKeyValueConcurrentMap<>();
|
||||
/**
|
||||
* 方法缓存
|
||||
*/
|
||||
private static final WeakConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------- Constructor
|
||||
|
||||
@ -89,7 +77,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.computeIfAbsent(beanClass, () -> getConstructorsDirectly(beanClass));
|
||||
return (Constructor<T>[]) CONSTRUCTORS_CACHE.computeIfAbsent(beanClass, (key) -> getConstructorsDirectly(beanClass));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,7 +166,7 @@ public class ReflectUtil {
|
||||
*/
|
||||
public static Field[] getFields(Class<?> beanClass) throws SecurityException {
|
||||
Assert.notNull(beanClass);
|
||||
return FIELDS_CACHE.computeIfAbsent(beanClass, () -> getFieldsDirectly(beanClass, true));
|
||||
return FIELDS_CACHE.computeIfAbsent(beanClass, (key) -> getFieldsDirectly(beanClass, true));
|
||||
}
|
||||
|
||||
|
||||
@ -673,7 +661,7 @@ public class ReflectUtil {
|
||||
public static Method[] getMethods(Class<?> beanClass) throws SecurityException {
|
||||
Assert.notNull(beanClass);
|
||||
return METHODS_CACHE.computeIfAbsent(beanClass,
|
||||
() -> getMethodsDirectly(beanClass, true, true));
|
||||
(key) -> getMethodsDirectly(beanClass, true, true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -794,7 +794,6 @@ public class URLUtil extends URLEncodeUtil {
|
||||
*
|
||||
* @param url URL
|
||||
* @return 长度
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static long size(final URL url) {
|
||||
if (URLUtil.isFileURL(url)) {
|
||||
@ -826,7 +825,6 @@ public class URLUtil extends URLEncodeUtil {
|
||||
* 如果连接为JNLP方式,则打开缓存
|
||||
*
|
||||
* @param con {@link URLConnection}
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static void useCachesIfNecessary(final URLConnection con) {
|
||||
con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP"));
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
package cn.hutool.core.map;
|
||||
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
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 static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class WeakConcurrentMapTest {
|
||||
|
||||
@Test
|
||||
public void putAndGetTest(){
|
||||
final WeakConcurrentMap<Object, Object> map = new WeakConcurrentMap<>();
|
||||
final WeakKeyValueConcurrentMap<Object, Object> map = new WeakKeyValueConcurrentMap<>();
|
||||
Object
|
||||
key1 = new Object(), value1 = new Object(),
|
||||
key2 = new Object(), value2 = new Object(),
|
||||
@ -41,7 +44,7 @@ public class WeakConcurrentMapTest {
|
||||
|
||||
@Test
|
||||
public void getConcurrencyTest(){
|
||||
final WeakConcurrentMap<String, String> cache = new WeakConcurrentMap<>();
|
||||
final WeakKeyValueConcurrentMap<String, String> cache = new WeakKeyValueConcurrentMap<>();
|
||||
final ConcurrencyTester tester = new ConcurrencyTester(9000);
|
||||
tester.test(()-> cache.computeIfAbsent("aaa" + RandomUtil.randomInt(2), (key)-> "aaaValue"));
|
||||
|
||||
|
||||
@ -8,21 +8,18 @@ import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.lang.test.bean.ExamInfoDict;
|
||||
import cn.hutool.core.util.ClassUtilTest.TestSubClass;
|
||||
import lombok.Data;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.FieldNameConstants;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* 反射工具类单元测试
|
||||
*
|
||||
@ -100,9 +97,9 @@ public class ReflectUtilTest {
|
||||
// 如果子类与父类中存在同名字段,则这两个字段同时存在,子类字段在前,父类字段在后。
|
||||
fields = ReflectUtil.getFields(TestSubUser.class);
|
||||
assertEquals(4, fields.length);
|
||||
List<Field> idFieldList = Arrays.asList(fields).stream().filter(f -> Objects.equals(f.getName(), TestSubUser.Fields.id)).collect(Collectors.toList());
|
||||
List<Field> idFieldList = Arrays.stream(fields).filter(f -> Objects.equals(f.getName(), TestSubUser.Fields.id)).collect(Collectors.toList());
|
||||
Field firstIdField = CollUtil.getFirst(idFieldList);
|
||||
assertEquals(true, Objects.equals(firstIdField.getDeclaringClass().getName(), TestSubUser.class.getName()));
|
||||
assertEquals(firstIdField.getDeclaringClass().getName(), TestSubUser.class.getName());
|
||||
}
|
||||
|
||||
@Data
|
||||
@ -111,6 +108,7 @@ public class ReflectUtilTest {
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@FieldNameConstants
|
||||
static class TestSubUser extends TestBaseEntity {
|
||||
@ -372,4 +370,5 @@ public class ReflectUtilTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package cn.hutool.extra.cglib;
|
||||
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
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 WeakConcurrentMap<String, BeanCopier> cache = new WeakConcurrentMap<>();
|
||||
private final WeakKeyValueConcurrentMap<String, BeanCopier> cache = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素
|
||||
|
||||
@ -308,7 +308,6 @@ public class JakartaServletUtil {
|
||||
*
|
||||
* @param request 请求对象{@link HttpServletRequest}
|
||||
* @return header值
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static Map<String, List<String>> getHeadersMap(final HttpServletRequest request) {
|
||||
final Map<String, List<String>> headerMap = new LinkedHashMap<>();
|
||||
|
||||
@ -308,7 +308,6 @@ public class ServletUtil {
|
||||
*
|
||||
* @param request 请求对象{@link HttpServletRequest}
|
||||
* @return header值
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static Map<String, List<String>> getHeadersMap(final HttpServletRequest request) {
|
||||
final Map<String, List<String>> headerMap = new LinkedHashMap<>();
|
||||
|
||||
@ -1,16 +1,9 @@
|
||||
package cn.hutool.script;
|
||||
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
import javax.script.*;
|
||||
|
||||
/**
|
||||
* 脚本工具类
|
||||
@ -20,7 +13,7 @@ import javax.script.ScriptException;
|
||||
public class ScriptUtil {
|
||||
|
||||
private static final ScriptEngineManager MANAGER = new ScriptEngineManager();
|
||||
private static final WeakConcurrentMap<String, ScriptEngine> CACHE = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<String, ScriptEngine> CACHE = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获得单例的{@link ScriptEngine} 实例
|
||||
@ -29,7 +22,7 @@ public class ScriptUtil {
|
||||
* @return {@link ScriptEngine} 实例
|
||||
*/
|
||||
public static ScriptEngine getScript(String nameOrExtOrMime) {
|
||||
return CACHE.computeIfAbsent(nameOrExtOrMime, () -> createScript(nameOrExtOrMime));
|
||||
return CACHE.computeIfAbsent(nameOrExtOrMime, (key) -> createScript(nameOrExtOrMime));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user