diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java
index b67db11f6..fac15387e 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/MapUtil.java
@@ -1056,6 +1056,21 @@ public class MapUtil extends MapGetUtil {
return removeIf(map, entry -> null == entry.getValue());
}
+ /**
+ * 去除Map中值为指定值的键值对
+ * 注意:此方法在传入的Map上直接修改。
+ *
+ * @param key的类型
+ * @param value的类型
+ * @param map Map
+ * @param value 给定值
+ * @return map
+ * @since 6.0.0
+ */
+ public static Map removeByValue(final Map map, final V value) {
+ return removeIf(map, entry -> ObjUtil.equals(value, entry.getValue()));
+ }
+
/**
* 去除Map中值为{@code null}的键值对
* 注意:此方法在传入的Map上直接修改。
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/ReferenceConcurrentMap.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/ReferenceConcurrentMap.java
new file mode 100644
index 000000000..6389fef0c
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/ReferenceConcurrentMap.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2023-2024. looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * https://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.core.map.reference;
+
+import org.dromara.hutool.core.map.MapUtil;
+import org.dromara.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 键类型
+ * @param 值类型
+ * @author looly
+ */
+public abstract class ReferenceConcurrentMap implements ConcurrentMap, Iterable>, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ final ConcurrentMap, Reference> raw;
+ private final ReferenceQueue lastKeyQueue;
+ private final ReferenceQueue lastValueQueue;
+ /**
+ * 回收监听
+ */
+ private BiConsumer, Reference extends V>> purgeListener;
+
+ // region 构造
+
+ /**
+ * 构造
+ *
+ * @param raw {@link ConcurrentMap}实现
+ */
+ public ReferenceConcurrentMap(final ConcurrentMap, Reference> raw) {
+ this.raw = raw;
+ lastKeyQueue = new ReferenceQueue<>();
+ lastValueQueue = new ReferenceQueue<>();
+ }
+ // endregion
+
+ /**
+ * 设置对象回收清除监听
+ *
+ * @param purgeListener 监听函数
+ */
+ public void setPurgeListener(final BiConsumer, Reference 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 Reference vReference = this.raw.put(wrapKey(key), wrapValue(value));
+ return unwrap(vReference);
+ }
+
+ @Override
+ public V putIfAbsent(final K key, final V value) {
+ this.purgeStale();
+ final Reference 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 Reference 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) {
+ this.purgeStale();
+ final Reference vReference = this.raw.computeIfAbsent(wrapKey(key),
+ kReference -> wrapValue(mappingFunction.apply(unwrap(kReference))));
+ return unwrap(vReference);
+ }
+
+ @Override
+ public V computeIfPresent(final K key, final BiFunction super K, ? super V, ? extends V> remappingFunction) {
+ this.purgeStale();
+ final Reference vReference = this.raw.computeIfPresent(wrapKey(key),
+ (kReference, vReference1) -> wrapValue(remappingFunction.apply(unwrap(kReference), unwrap(vReference1))));
+ return unwrap(vReference);
+ }
+
+ @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 keySet() {
+ this.purgeStale();
+ final Set> referenceSet = this.raw.keySet();
+ return new AbstractSet() {
+ @Override
+ public Iterator iterator() {
+ final Iterator> referenceIter = referenceSet.iterator();
+ return new Iterator() {
+ @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 values() {
+ this.purgeStale();
+ final Collection> referenceValues = this.raw.values();
+ return new AbstractCollection() {
+ @Override
+ public Iterator iterator() {
+ final Iterator> referenceIter = referenceValues.iterator();
+ return new Iterator() {
+ @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> entrySet() {
+ this.purgeStale();
+ final Set, Reference>> referenceEntrySet = this.raw.entrySet();
+ return new AbstractSet>() {
+ @Override
+ public Iterator> iterator() {
+ final Iterator, Reference>> referenceIter = referenceEntrySet.iterator();
+ return new Iterator>() {
+ @Override
+ public boolean hasNext() {
+ return referenceIter.hasNext();
+ }
+
+ @Override
+ public Entry next() {
+ final Entry, Reference> next = referenceIter.next();
+ return new Entry() {
+ @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> 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() {
+ Reference extends K> key;
+ Reference extends V> value;
+
+ // 清除无效key对应键值对
+ while ((key = this.lastKeyQueue.poll()) != null) {
+ value = this.raw.remove(key);
+ if (null != purgeListener) {
+ purgeListener.accept(key, value);
+ }
+ }
+
+ // 清除无效value对应的键值对
+ while ((value = this.lastValueQueue.poll()) != null) {
+ MapUtil.removeByValue(this.raw, (Reference) value);
+ if (null != purgeListener) {
+ purgeListener.accept(null, value);
+ }
+ }
+ }
+
+ /**
+ * 根据Reference类型构建key对应的{@link Reference}
+ *
+ * @param key 键
+ * @param queue {@link ReferenceQueue}
+ * @return {@link Reference}
+ */
+ abstract Reference wrapKey(final K key, final ReferenceQueue super K> queue);
+
+ /**
+ * 根据Reference类型构建value对应的{@link Reference}
+ *
+ * @param value 值
+ * @param queue {@link ReferenceQueue}
+ * @return {@link Reference}
+ */
+ abstract Reference wrapValue(final V value, final ReferenceQueue super V> queue);
+
+ /**
+ * 根据Reference类型构建key对应的{@link Reference}
+ *
+ * @param key 键
+ * @return {@link Reference}
+ */
+ @SuppressWarnings("unchecked")
+ private Reference wrapKey(final Object key) {
+ return wrapKey((K) key, this.lastKeyQueue);
+ }
+
+ /**
+ * 根据Reference类型构建value对应的{@link Reference}
+ *
+ * @param value 键
+ * @return {@link Reference}
+ */
+ @SuppressWarnings("unchecked")
+ private Reference wrapValue(final Object value) {
+ return wrapValue((V) value, this.lastValueQueue);
+ }
+
+ /**
+ * 去包装对象
+ *
+ * @param 对象类型
+ * @param obj 对象
+ * @return 值
+ */
+ private static T unwrap(final Reference obj) {
+ return ReferenceUtil.get(obj);
+ }
+}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/ReferenceKeyConcurrentMap.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/ReferenceKeyConcurrentMap.java
index 2bf528080..24ac7f24b 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/ReferenceKeyConcurrentMap.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/ReferenceKeyConcurrentMap.java
@@ -13,20 +13,12 @@
package org.dromara.hutool.core.map.reference;
import org.dromara.hutool.core.collection.CollUtil;
-import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.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.*;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -244,80 +236,10 @@ public class ReferenceKeyConcurrentMap implements ConcurrentMap, Ite
private Reference ofKey(final K key, final ReferenceQueue super K> queue) {
switch (keyType) {
case WEAK:
- return new WeakKey<>(key, queue);
+ return new WeakObj<>(key, queue);
case SOFT:
- return new SoftKey<>(key, queue);
+ return new SoftObj<>(key, queue);
}
throw new IllegalArgumentException("Unsupported key type: " + keyType);
}
-
- /**
- * 弱键
- *
- * @param 键类型
- */
- private static class WeakKey extends WeakReference {
- private final int hashCode;
-
- /**
- * 构造
- *
- * @param key 原始Key,不能为{@code null}
- * @param queue {@link ReferenceQueue}
- */
- WeakKey(final K key, final ReferenceQueue super K> queue) {
- super(key, queue);
- hashCode = key.hashCode();
- }
-
- @Override
- public int hashCode() {
- return hashCode;
- }
-
- @Override
- public boolean equals(final Object other) {
- if (other == this) {
- return true;
- } else if (other instanceof WeakKey) {
- return ObjUtil.equals(((WeakKey>) other).get(), get());
- }
- return false;
- }
- }
-
- /**
- * 弱键
- *
- * @param 键类型
- */
- private static class SoftKey extends SoftReference {
- private final int hashCode;
-
- /**
- * 构造
- *
- * @param key 原始Key,不能为{@code null}
- * @param queue {@link ReferenceQueue}
- */
- SoftKey(final K key, final ReferenceQueue super K> queue) {
- super(key, queue);
- hashCode = key.hashCode();
- }
-
- @Override
- public int hashCode() {
- return hashCode;
- }
-
- @Override
- public boolean equals(final Object other) {
- if (other == this) {
- return true;
- } else if (other instanceof SoftKey) {
- return ObjUtil.equals(((SoftKey>) other).get(), get());
- }
- return false;
- }
- }
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/SoftObj.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/SoftObj.java
new file mode 100644
index 000000000..682e39d38
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/SoftObj.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024. looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * https://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.core.map.reference;
+
+import org.dromara.hutool.core.util.ObjUtil;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+
+/**
+ * 弱键
+ *
+ * @param 键类型
+ */
+public class SoftObj extends SoftReference {
+ private final int hashCode;
+
+ /**
+ * 构造
+ *
+ * @param key 原始Key,不能为{@code null}
+ * @param queue {@link ReferenceQueue}
+ */
+ public SoftObj(final T key, final ReferenceQueue super T> queue) {
+ super(key, queue);
+ hashCode = key.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (other == this) {
+ return true;
+ } else if (other instanceof SoftObj) {
+ return ObjUtil.equals(((SoftObj>) other).get(), get());
+ }
+ return false;
+ }
+}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/WeakObj.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/WeakObj.java
new file mode 100644
index 000000000..efd7ce801
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/WeakObj.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024. looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * https://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.core.map.reference;
+
+import org.dromara.hutool.core.util.ObjUtil;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+
+/**
+ * 弱键
+ *
+ * @param 键类型
+ */
+public class WeakObj extends WeakReference {
+ private final int hashCode;
+
+ /**
+ * 构造
+ *
+ * @param key 原始Key,不能为{@code null}
+ * @param queue {@link ReferenceQueue}
+ */
+ public WeakObj(final T key, final ReferenceQueue super T> queue) {
+ super(key, queue);
+ hashCode = key.hashCode();
+ }
+
+ @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;
+ }
+}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/util/ReferenceUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/util/ReferenceUtil.java
index a3eea8268..db56d1f82 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/util/ReferenceUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/util/ReferenceUtil.java
@@ -35,8 +35,8 @@ public class ReferenceUtil {
/**
* 获得引用
*
- * @param 被引用对象类型
- * @param type 引用类型枚举
+ * @param 被引用对象类型
+ * @param type 引用类型枚举
* @param referent 被引用对象
* @return {@link Reference}
*/
@@ -47,35 +47,50 @@ public class ReferenceUtil {
/**
* 获得引用
*
- * @param 被引用对象类型
- * @param type 引用类型枚举
+ * @param 被引用对象类型
+ * @param type 引用类型枚举
* @param referent 被引用对象
- * @param queue 引用队列
+ * @param queue 引用队列
* @return {@link Reference}
*/
public static Reference of(final ReferenceType type, final T referent, final ReferenceQueue queue) {
switch (type) {
- case SOFT:
- return new SoftReference<>(referent, queue);
- case WEAK:
- return new WeakReference<>(referent, queue);
- case PHANTOM:
- return new PhantomReference<>(referent, queue);
- default:
- return null;
+ case SOFT:
+ return new SoftReference<>(referent, queue);
+ case WEAK:
+ return new WeakReference<>(referent, queue);
+ case PHANTOM:
+ return new PhantomReference<>(referent, queue);
+ default:
+ return null;
}
}
+ /**
+ * {@code null}全的解包获取原始对象
+ *
+ * @param 对象类型
+ * @param obj Reference对象
+ * @return 原始对象 or {@code null}
+ * @since 6.0.0
+ */
+ public static T get(final Reference obj) {
+ return ObjUtil.apply(obj, Reference::get);
+ }
+
/**
* 引用类型
*
* @author looly
- *
*/
public enum ReferenceType {
- /** 软引用,在GC报告内存不足时会被GC回收 */
+ /**
+ * 软引用,在GC报告内存不足时会被GC回收
+ */
SOFT,
- /** 弱引用,在GC时发现弱引用会回收其对象 */
+ /**
+ * 弱引用,在GC时发现弱引用会回收其对象
+ */
WEAK,
/**
* 虚引用,在GC时发现虚引用对象,会将{@link PhantomReference}插入{@link ReferenceQueue}。