diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDescCache.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDescCache.java
index f8976b036..1ea29b078 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDescCache.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDescCache.java
@@ -12,9 +12,10 @@
package org.dromara.hutool.core.bean;
-import org.dromara.hutool.core.func.SerSupplier;
import org.dromara.hutool.core.map.reference.WeakConcurrentMap;
+import java.util.function.Supplier;
+
/**
* Bean属性缓存
* 缓存用于防止多次反射造成的性能问题
@@ -27,18 +28,20 @@ public enum BeanDescCache {
*/
INSTANCE;
- private final WeakConcurrentMap, StrictBeanDesc> bdCache = new WeakConcurrentMap<>();
+ private final WeakConcurrentMap, BeanDesc> bdCache = new WeakConcurrentMap<>();
/**
- * 获得属性名和{@link StrictBeanDesc}Map映射
+ * 获得属性名和{@link BeanDesc}Map映射
*
* @param beanClass Bean的类
* @param supplier 对象不存在时创建对象的函数
- * @return 属性名和 {@link StrictBeanDesc}映射
+ * @param BeanDesc子类
+ * @return 属性名和 {@link BeanDesc}映射
* @since 5.4.2
*/
- public StrictBeanDesc getBeanDesc(final Class> beanClass, final SerSupplier supplier) {
- return bdCache.computeIfAbsent(beanClass, (key) -> supplier.get());
+ @SuppressWarnings("unchecked")
+ public T getBeanDesc(final Class> beanClass, final Supplier supplier) {
+ return (T) bdCache.computeIfAbsent(beanClass, (key) -> supplier.get());
}
/**
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
index 32aae0301..996819c50 100644
--- 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
@@ -136,18 +136,40 @@ public abstract class ReferenceConcurrentMap implements ConcurrentMap mappingFunction) {
- this.purgeStale();
- final Ref vReference = this.raw.computeIfAbsent(wrapKey(key),
- kReference -> wrapValue(mappingFunction.apply(unwrap(kReference))));
- return unwrap(vReference);
+ V result = null;
+ while(null == result){
+ this.purgeStale();
+ final Ref 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) {
- this.purgeStale();
- final Ref vReference = this.raw.computeIfPresent(wrapKey(key),
- (kReference, vReference1) -> wrapValue(remappingFunction.apply(unwrap(kReference), unwrap(vReference1))));
- return unwrap(vReference);
+ V result = null;
+ while(null == result){
+ this.purgeStale();
+ final Ref 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
@@ -358,6 +380,9 @@ public abstract class ReferenceConcurrentMap implements ConcurrentMap wrapValue(final Object value) {
+ if(null == value){
+ return (Ref) NullRef.NULL;
+ }
return wrapValue((V) value, this.lastValueQueue);
}
@@ -371,4 +396,14 @@ public abstract class ReferenceConcurrentMap implements ConcurrentMap T unwrap(final Ref 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;
+ }
+ }
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/WeakKeyConcurrentMap.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/WeakKeyConcurrentMap.java
new file mode 100644
index 000000000..2fe408e97
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/reference/WeakKeyConcurrentMap.java
@@ -0,0 +1,60 @@
+/*
+ * 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.lang.ref.Ref;
+import org.dromara.hutool.core.lang.ref.StrongObj;
+import org.dromara.hutool.core.lang.ref.WeakObj;
+import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
+
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * 线程安全的WeakMap实现
+ * 键为Weak引用,即,在GC时发现弱引用会回收其对象
+ *
+ * @param 键类型
+ * @param 值类型
+ * @author looly
+ * @since 6.0.0
+ */
+public class WeakKeyConcurrentMap extends ReferenceConcurrentMap {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 构造
+ */
+ public WeakKeyConcurrentMap() {
+ this(new SafeConcurrentHashMap<>());
+ }
+
+ /**
+ * 构造
+ *
+ * @param raw {@link ConcurrentMap}实现
+ */
+ public WeakKeyConcurrentMap(final ConcurrentMap[, Ref> raw) {
+ super(raw);
+ }
+
+ @Override
+ Ref wrapKey(final K key, final ReferenceQueue super K> queue) {
+ return new WeakObj<>(key, queue);
+ }
+
+ @Override
+ Ref wrapValue(final V value, final ReferenceQueue super V> queue) {
+ return new StrongObj<>(value);
+ }
+}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI8IUTBTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI8IUTBTest.java
index 9e3610baa..f9e723c04 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI8IUTBTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI8IUTBTest.java
@@ -1,10 +1,12 @@
package org.dromara.hutool.core.date;
import org.dromara.hutool.core.lang.Console;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
public class IssueI8IUTBTest {
@Test
+ @Disabled
void parseTest() {
final DateTime parse = DateUtil.parse("May 8, 2009 5:57:51 PM");
Console.log(parse);
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/map/reference/WeakConcurrentMapTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/map/reference/WeakConcurrentMapTest.java
index 49bedd841..c6c7b73da 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/map/reference/WeakConcurrentMapTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/map/reference/WeakConcurrentMapTest.java
@@ -1,7 +1,5 @@
package org.dromara.hutool.core.map.reference;
-import org.dromara.hutool.core.thread.ThreadUtil;
-import org.dromara.hutool.core.util.RandomUtil;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@@ -29,20 +27,21 @@ public class WeakConcurrentMapTest {
assertTrue(map.containsKey("key1"));
assertTrue(map.containsKey("key2"));
+
+ // null值
+ String s = map.computeIfAbsent("key3", key -> null);
+ assertNull(s);
+
+ // 允许key为null
+ s = map.computeIfAbsent(null, key -> null);
+ assertNull(s);
}
+ @SuppressWarnings("StringOperationCanBeSimplified")
@Test
void computeIfAbsentTest() {
final WeakConcurrentMap map = new WeakConcurrentMap<>();
-
- for (int i = 0; i < 1000; i++) {
- ThreadUtil.execute(()->{
- final String s = map.computeIfAbsent(RandomUtil.randomString(1), key -> "value1");
- assertEquals("value1", s);
- });
- }
-
- ThreadUtil.sleep(500);
- assertFalse(map.isEmpty());
+ final String value = map.computeIfAbsent("key1", key -> new String("value1"));
+ assertNotNull(value);
}
}
]