From 514dea21b250aa258eb78157f137d4338aa3d1c1 Mon Sep 17 00:00:00 2001 From: lzpeng723 <1500913306@qq.com> Date: Sat, 6 Nov 2021 09:55:44 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E7=94=B1=E4=BA=8E=20FileObjectResource=20?= =?UTF-8?q?=E7=9A=84=E6=B5=81=E4=B8=8D=E8=83=BD=E8=AF=BB=E5=8F=96=E5=A4=9A?= =?UTF-8?q?=E6=AC=A1=EF=BC=8CResourceClassLoader=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BC=93=E5=AD=98,=E5=90=8C=E4=B8=80=E4=B8=AA=E7=B1=BB?= =?UTF-8?q?=E5=8F=AA=E8=83=BD=E5=8A=A0=E8=BD=BD=E4=B8=80=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hutool/core/lang/ResourceClassLoader.java | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java b/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java index c7e11d7b7..a55aefc63 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java @@ -7,18 +7,24 @@ import cn.hutool.core.util.ObjectUtil; import java.security.SecureClassLoader; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * 资源类加载器,可以加载任意类型的资源类 * * @param {@link Resource}接口实现类 - * @author looly + * @author looly,lzpeng * @since 5.5.2 */ public class ResourceClassLoader extends SecureClassLoader { private final Map resourceMap; + /** + * 缓存已经加载的类 + */ + private final Map cacheClassMap; + /** * 构造 * @@ -28,6 +34,7 @@ public class ResourceClassLoader extends SecureClassLoader { public ResourceClassLoader(ClassLoader parentClassLoader, Map resourceMap) { super(ObjectUtil.defaultIfNull(parentClassLoader, ClassLoaderUtil.getClassLoader())); this.resourceMap = ObjectUtil.defaultIfNull(resourceMap, new HashMap<>()); + this.cacheClassMap = new ConcurrentHashMap<>(); } /** @@ -42,11 +49,21 @@ public class ResourceClassLoader extends SecureClassLoader { @Override protected Class findClass(String name) throws ClassNotFoundException { - final Resource resource = resourceMap.get(name); - if (null != resource) { - final byte[] bytes = resource.readBytes(); - return defineClass(name, bytes, 0, bytes.length); + final Class clazz = cacheClassMap.computeIfAbsent(name, className -> { + final Resource resource = resourceMap.get(name); + if (null != resource) { + final byte[] bytes = resource.readBytes(); + return defineClass(name, bytes, 0, bytes.length); + } + try { + return super.findClass(name); + } catch (ClassNotFoundException e) { + return null; + } + }); + if (clazz == null) { + throw new ClassNotFoundException(name); } - return super.findClass(name); + return clazz; } } From 3d06375871296fde373f0da128ef5950f61278e2 Mon Sep 17 00:00:00 2001 From: achao Date: Wed, 17 Nov 2021 22:23:17 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E7=9A=84=E5=B9=B6=E8=A1=8C=E6=B5=81=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/collection/CollStreamUtil.java | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java index 53243cb71..5fb7eb7fe 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java @@ -33,6 +33,25 @@ public class CollStreamUtil { return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); } + + /** + * 将collection转化为类型不变的map
+ * {@code Collection ----> Map} + * + * @param collection 需要转化的集合 + * @param key V类型转化为K类型的lambda方法 + * @param collection中的泛型 + * @param map中的key类型 + * @return 转化后的map + * @see #toIdentityMap toIdentityMap的并行流实现 + */ + public static Map toIdentityMapP(Collection collection, Function key) { + if (CollUtil.isEmpty(collection)) { + return Collections.emptyMap(); + } + return collection.parallelStream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); + } + /** * 将Collection转化为map(value类型与collection的泛型不同)
* {@code Collection -----> Map } @@ -52,6 +71,26 @@ public class CollStreamUtil { return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l)); } + /** + * @param collection 需要转化的集合 + * @param key E类型转化为K类型的lambda方法 + * @param value E类型转化为V类型的lambda方法 + * @param collection中的泛型 + * @param map中的key类型 + * @param map中的value类型 + * @return 转化后的map + * @see #toMap toMap的并行流实现 + * 将Collection转化为map(value类型与collection的泛型不同)
+ * {@code Collection -----> Map } + */ + public static Map toMapP(Collection collection, Function key, Function value) { + if (CollUtil.isEmpty(collection)) { + return Collections.emptyMap(); + } + return collection.parallelStream().collect(Collectors.toMap(key, value, (l, r) -> l)); + } + + /** * 将collection按照规则(比如有相同的班级id)分类成map
* {@code Collection -------> Map> } @@ -71,6 +110,26 @@ public class CollStreamUtil { .collect(Collectors.groupingBy(key, Collectors.toList())); } + /** + * 将collection按照规则(比如有相同的班级id)分类成map
+ * {@code Collection -------> Map> } + * + * @param collection 需要分类的集合 + * @param key 分类的规则 + * @param collection中的泛型 + * @param map中的key类型 + * @return 分类后的map + * @see #groupByKey groupByKey的并行流实现 + */ + public static Map> groupByKeyP(Collection collection, Function key) { + if (CollUtil.isEmpty(collection)) { + return Collections.emptyMap(); + } + return collection + .parallelStream() + .collect(Collectors.groupingBy(key, Collectors.toList())); + } + /** * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
* {@code Collection ---> Map>> } @@ -92,6 +151,29 @@ public class CollStreamUtil { .collect(Collectors.groupingBy(key1, Collectors.groupingBy(key2, Collectors.toList()))); } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map>> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param 集合元素类型 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @return 分类后的map + * @see #groupBy2Key groupBy2Key的并行实现 + */ + public static Map>> groupBy2KeyP(Collection collection, Function key1, Function key2) { + if (CollUtil.isEmpty(collection)) { + return Collections.emptyMap(); + } + return collection + .parallelStream() + .collect(Collectors.groupingBy(key1, Collectors.groupingBy(key2, Collectors.toList()))); + } + /** * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
* {@code Collection ---> Map> } @@ -113,6 +195,28 @@ public class CollStreamUtil { .collect(Collectors.groupingBy(key1, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); } + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @param collection中的泛型 + * @return 分类后的map + * @see #group2Map 的并行实现 + */ + public static Map> group2MapP(Collection collection, Function key1, Function key2) { + if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { + return Collections.emptyMap(); + } + return collection + .parallelStream() + .collect(Collectors.groupingBy(key1, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); + } + /** * 将collection转化为List集合,但是两者的泛型不同
* {@code Collection ------> List } @@ -134,6 +238,28 @@ public class CollStreamUtil { .collect(Collectors.toList()); } + /** + * 将collection转化为List集合,但是两者的泛型不同
+ * {@code Collection ------> List } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为list泛型的lambda表达式 + * @param collection中的泛型 + * @param List中的泛型 + * @return 转化后的list + * @see #toList 的并行实现 + */ + public static List toListP(Collection collection, Function function) { + if (CollUtil.isEmpty(collection)) { + return Collections.emptyList(); + } + return collection + .parallelStream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + /** * 将collection转化为Set集合,但是两者的泛型不同
* {@code Collection ------> Set } @@ -155,6 +281,28 @@ public class CollStreamUtil { .collect(Collectors.toSet()); } + /** + * 将collection转化为Set集合,但是两者的泛型不同
+ * {@code Collection ------> Set } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为set泛型的lambda表达式 + * @param collection中的泛型 + * @param Set中的泛型 + * @return 转化后的Set + * @see #toSetP 的并行实现 + */ + public static Set toSetP(Collection collection, Function function) { + if (CollUtil.isEmpty(collection) || function == null) { + return Collections.emptySet(); + } + return collection + .parallelStream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + /** * 合并两个相同key类型的map From 8b3f34674288db51d6b7462e53ebe562a0457388 Mon Sep 17 00:00:00 2001 From: VampireAchao Date: Thu, 18 Nov 2021 17:33:46 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0CollStreamUtil=E5=B9=B6?= =?UTF-8?q?=E8=A1=8C=E6=B5=81=E7=9B=B8=E5=85=B3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/collection/CollStreamUtil.java | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java index 53243cb71..9be5a6413 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java @@ -33,6 +33,29 @@ public class CollStreamUtil { return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); } + + /** + * 将collection转化为类型不变的map
+ * {@code Collection ----> Map} + * + * @param collection 需要转化的集合 + * @param key V类型转化为K类型的lambda方法 + * @param isParallel 是否并行流 + * @param collection中的泛型 + * @param map中的key类型 + * @return 转化后的map + * @see #toIdentityMap toIdentityMap的并行流实现 + */ + public static Map toIdentityMap(Collection collection, Function key, boolean isParallel) { + if (false == isParallel) { + return toIdentityMap(collection, key); + } + if (CollUtil.isEmpty(collection)) { + return Collections.emptyMap(); + } + return collection.parallelStream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); + } + /** * 将Collection转化为map(value类型与collection的泛型不同)
* {@code Collection -----> Map } @@ -52,6 +75,30 @@ public class CollStreamUtil { return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l)); } + /** + * @param collection 需要转化的集合 + * @param key E类型转化为K类型的lambda方法 + * @param value E类型转化为V类型的lambda方法 + * @param isParallel 是否并行流 + * @param collection中的泛型 + * @param map中的key类型 + * @param map中的value类型 + * @return 转化后的map + * @see #toMap toMap的并行流实现 + * 将Collection转化为map(value类型与collection的泛型不同)
+ * {@code Collection -----> Map } + */ + public static Map toMap(Collection collection, Function key, Function value, boolean isParallel) { + if (false == isParallel) { + return toMap(collection, key, value); + } + if (CollUtil.isEmpty(collection)) { + return Collections.emptyMap(); + } + return collection.parallelStream().collect(Collectors.toMap(key, value, (l, r) -> l)); + } + + /** * 将collection按照规则(比如有相同的班级id)分类成map
* {@code Collection -------> Map> } @@ -71,6 +118,30 @@ public class CollStreamUtil { .collect(Collectors.groupingBy(key, Collectors.toList())); } + /** + * 将collection按照规则(比如有相同的班级id)分类成map
+ * {@code Collection -------> Map> } + * + * @param collection 需要分类的集合 + * @param key 分类的规则 + * @param isParallel 是否并行流 + * @param collection中的泛型 + * @param map中的key类型 + * @return 分类后的map + * @see #groupByKey groupByKey的并行流实现 + */ + public static Map> groupByKey(Collection collection, Function key, boolean isParallel) { + if (false == isParallel) { + return groupByKey(collection, key); + } + if (CollUtil.isEmpty(collection)) { + return Collections.emptyMap(); + } + return collection + .parallelStream() + .collect(Collectors.groupingBy(key, Collectors.toList())); + } + /** * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
* {@code Collection ---> Map>> } @@ -92,6 +163,33 @@ public class CollStreamUtil { .collect(Collectors.groupingBy(key1, Collectors.groupingBy(key2, Collectors.toList()))); } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map>> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param isParallel 是否并行流 + * @param 集合元素类型 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @return 分类后的map + * @see #groupBy2Key groupBy2Key的并行实现 + */ + public static Map>> groupBy2Key(Collection collection, Function key1, Function key2, boolean isParallel) { + if (false == isParallel) { + return groupBy2Key(collection, key1, key2); + } + if (CollUtil.isEmpty(collection)) { + return Collections.emptyMap(); + } + return collection + .parallelStream() + .collect(Collectors.groupingBy(key1, Collectors.groupingBy(key2, Collectors.toList()))); + } + /** * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
* {@code Collection ---> Map> } @@ -113,6 +211,32 @@ public class CollStreamUtil { .collect(Collectors.groupingBy(key1, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); } + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param isParallel 是否并行流 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @param collection中的泛型 + * @return 分类后的map + * @see #group2Map 的并行实现 + */ + public static Map> group2Map(Collection collection, Function key1, Function key2, boolean isParallel) { + if (false == isParallel) { + return group2Map(collection, key1, key2); + } + if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { + return Collections.emptyMap(); + } + return collection + .parallelStream() + .collect(Collectors.groupingBy(key1, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); + } + /** * 将collection转化为List集合,但是两者的泛型不同
* {@code Collection ------> List } @@ -134,6 +258,32 @@ public class CollStreamUtil { .collect(Collectors.toList()); } + /** + * 将collection转化为List集合,但是两者的泛型不同
+ * {@code Collection ------> List } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为list泛型的lambda表达式 + * @param isParallel 是否并行流 + * @param collection中的泛型 + * @param List中的泛型 + * @return 转化后的list + * @see #toList 的并行实现 + */ + public static List toList(Collection collection, Function function, boolean isParallel) { + if (false == isParallel) { + return toList(collection, function); + } + if (CollUtil.isEmpty(collection)) { + return Collections.emptyList(); + } + return collection + .parallelStream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + /** * 将collection转化为Set集合,但是两者的泛型不同
* {@code Collection ------> Set } @@ -155,6 +305,32 @@ public class CollStreamUtil { .collect(Collectors.toSet()); } + /** + * 将collection转化为Set集合,但是两者的泛型不同
+ * {@code Collection ------> Set } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为set泛型的lambda表达式 + * @param isParallel 是否并行流 + * @param collection中的泛型 + * @param Set中的泛型 + * @return 转化后的Set + * @see #toSet 的并行实现 + */ + public static Set toSet(Collection collection, Function function, boolean isParallel) { + if (false == isParallel) { + return toSet(collection, function); + } + if (CollUtil.isEmpty(collection) || function == null) { + return Collections.emptySet(); + } + return collection + .parallelStream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + /** * 合并两个相同key类型的map From 6588db784152e66ce752958ecd869ca7707f2de9 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 18 Nov 2021 23:50:37 +0800 Subject: [PATCH 4/8] add methods --- CHANGELOG.md | 3 +- .../core/collection/CollStreamUtil.java | 109 ++++-------------- 2 files changed, 23 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 872cd4e56..66f613e63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.17 (2021-11-16) +# 5.7.17 (2021-11-18) ### 🐣新特性 * 【core 】 增加AsyncUtil(pr#457@Gitee) @@ -17,6 +17,7 @@ * 【core 】 改进TextFinder,支持限制结束位置及反向查找模式 * 【core 】 Opt增加部分方法(pr#459@Gitee) * 【core 】 增加DefaultCloneable(pr#459@Gitee) +* 【core 】 CollStreamUtil增加是否并行的重载(pr#467@Gitee) * ### 🐞Bug修复 * 【core 】 修复FileResource构造fileName参数无效问题(issue#1942@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java index 9be5a6413..693be7936 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollStreamUtil.java @@ -2,6 +2,7 @@ package cn.hutool.core.collection; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.stream.StreamUtil; import java.util.*; import java.util.function.BiFunction; @@ -27,10 +28,7 @@ public class CollStreamUtil { * @return 转化后的map */ public static Map toIdentityMap(Collection collection, Function key) { - if (CollUtil.isEmpty(collection)) { - return Collections.emptyMap(); - } - return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); + return toIdentityMap(collection, key, false); } @@ -44,16 +42,13 @@ public class CollStreamUtil { * @param collection中的泛型 * @param map中的key类型 * @return 转化后的map - * @see #toIdentityMap toIdentityMap的并行流实现 */ public static Map toIdentityMap(Collection collection, Function key, boolean isParallel) { - if (false == isParallel) { - return toIdentityMap(collection, key); - } if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return collection.parallelStream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); + return StreamUtil.of(collection, isParallel) + .collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); } /** @@ -69,10 +64,7 @@ public class CollStreamUtil { * @return 转化后的map */ public static Map toMap(Collection collection, Function key, Function value) { - if (CollUtil.isEmpty(collection)) { - return Collections.emptyMap(); - } - return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l)); + return toMap(collection, key, value, false); } /** @@ -84,18 +76,12 @@ public class CollStreamUtil { * @param map中的key类型 * @param map中的value类型 * @return 转化后的map - * @see #toMap toMap的并行流实现 - * 将Collection转化为map(value类型与collection的泛型不同)
- * {@code Collection -----> Map } */ public static Map toMap(Collection collection, Function key, Function value, boolean isParallel) { - if (false == isParallel) { - return toMap(collection, key, value); - } if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return collection.parallelStream().collect(Collectors.toMap(key, value, (l, r) -> l)); + return StreamUtil.of(collection, isParallel).collect(Collectors.toMap(key, value, (l, r) -> l)); } @@ -110,12 +96,7 @@ public class CollStreamUtil { * @return 分类后的map */ public static Map> groupByKey(Collection collection, Function key) { - if (CollUtil.isEmpty(collection)) { - return Collections.emptyMap(); - } - return collection - .stream() - .collect(Collectors.groupingBy(key, Collectors.toList())); + return groupByKey(collection, key, false); } /** @@ -128,18 +109,12 @@ public class CollStreamUtil { * @param collection中的泛型 * @param map中的key类型 * @return 分类后的map - * @see #groupByKey groupByKey的并行流实现 */ public static Map> groupByKey(Collection collection, Function key, boolean isParallel) { - if (false == isParallel) { - return groupByKey(collection, key); - } if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return collection - .parallelStream() - .collect(Collectors.groupingBy(key, Collectors.toList())); + return StreamUtil.of(collection, isParallel).collect(Collectors.groupingBy(key, Collectors.toList())); } /** @@ -155,12 +130,7 @@ public class CollStreamUtil { * @return 分类后的map */ public static Map>> groupBy2Key(Collection collection, Function key1, Function key2) { - if (CollUtil.isEmpty(collection)) { - return Collections.emptyMap(); - } - return collection - .stream() - .collect(Collectors.groupingBy(key1, Collectors.groupingBy(key2, Collectors.toList()))); + return groupBy2Key(collection, key1, key2, false); } @@ -176,17 +146,13 @@ public class CollStreamUtil { * @param 第一个map中的key类型 * @param 第二个map中的key类型 * @return 分类后的map - * @see #groupBy2Key groupBy2Key的并行实现 */ - public static Map>> groupBy2Key(Collection collection, Function key1, Function key2, boolean isParallel) { - if (false == isParallel) { - return groupBy2Key(collection, key1, key2); - } + public static Map>> groupBy2Key(Collection collection, Function key1, + Function key2, boolean isParallel) { if (CollUtil.isEmpty(collection)) { return Collections.emptyMap(); } - return collection - .parallelStream() + return StreamUtil.of(collection, isParallel) .collect(Collectors.groupingBy(key1, Collectors.groupingBy(key2, Collectors.toList()))); } @@ -203,12 +169,7 @@ public class CollStreamUtil { * @return 分类后的map */ public static Map> group2Map(Collection collection, Function key1, Function key2) { - if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { - return Collections.emptyMap(); - } - return collection - .stream() - .collect(Collectors.groupingBy(key1, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); + return group2Map(collection, key1, key2, false); } /** @@ -223,17 +184,13 @@ public class CollStreamUtil { * @param 第二个map中的key类型 * @param collection中的泛型 * @return 分类后的map - * @see #group2Map 的并行实现 */ - public static Map> group2Map(Collection collection, Function key1, Function key2, boolean isParallel) { - if (false == isParallel) { - return group2Map(collection, key1, key2); - } + public static Map> group2Map(Collection collection, + Function key1, Function key2, boolean isParallel) { if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { return Collections.emptyMap(); } - return collection - .parallelStream() + return StreamUtil.of(collection, isParallel) .collect(Collectors.groupingBy(key1, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); } @@ -248,14 +205,7 @@ public class CollStreamUtil { * @return 转化后的list */ public static List toList(Collection collection, Function function) { - if (CollUtil.isEmpty(collection)) { - return Collections.emptyList(); - } - return collection - .stream() - .map(function) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + return toList(collection, function, false); } /** @@ -268,17 +218,12 @@ public class CollStreamUtil { * @param collection中的泛型 * @param List中的泛型 * @return 转化后的list - * @see #toList 的并行实现 */ public static List toList(Collection collection, Function function, boolean isParallel) { - if (false == isParallel) { - return toList(collection, function); - } if (CollUtil.isEmpty(collection)) { return Collections.emptyList(); } - return collection - .parallelStream() + return StreamUtil.of(collection, isParallel) .map(function) .filter(Objects::nonNull) .collect(Collectors.toList()); @@ -295,14 +240,7 @@ public class CollStreamUtil { * @return 转化后的Set */ public static Set toSet(Collection collection, Function function) { - if (CollUtil.isEmpty(collection) || function == null) { - return Collections.emptySet(); - } - return collection - .stream() - .map(function) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); + return toSet(collection, function, false); } /** @@ -315,17 +253,12 @@ public class CollStreamUtil { * @param collection中的泛型 * @param Set中的泛型 * @return 转化后的Set - * @see #toSet 的并行实现 */ public static Set toSet(Collection collection, Function function, boolean isParallel) { - if (false == isParallel) { - return toSet(collection, function); - } - if (CollUtil.isEmpty(collection) || function == null) { + if (CollUtil.isEmpty(collection)) { return Collections.emptySet(); } - return collection - .parallelStream() + return StreamUtil.of(collection, isParallel) .map(function) .filter(Objects::nonNull) .collect(Collectors.toSet()); From 6ed8588c83029d64e3dbd58732971e61c603e2e6 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 19 Nov 2021 00:32:51 +0800 Subject: [PATCH 5/8] add cache --- CHANGELOG.md | 3 +- .../hutool/core/lang/ResourceClassLoader.java | 42 ++++++++++--------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66f613e63..84418d6f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.17 (2021-11-18) +# 5.7.17 (2021-11-19) ### 🐣新特性 * 【core 】 增加AsyncUtil(pr#457@Gitee) @@ -18,6 +18,7 @@ * 【core 】 Opt增加部分方法(pr#459@Gitee) * 【core 】 增加DefaultCloneable(pr#459@Gitee) * 【core 】 CollStreamUtil增加是否并行的重载(pr#467@Gitee) +* 【core 】 ResourceClassLoader增加缓存(pr#1959@Github) * ### 🐞Bug修复 * 【core 】 修复FileResource构造fileName参数无效问题(issue#1942@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java b/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java index a55aefc63..383f6b98b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java @@ -7,23 +7,21 @@ import cn.hutool.core.util.ObjectUtil; import java.security.SecureClassLoader; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * 资源类加载器,可以加载任意类型的资源类 * * @param {@link Resource}接口实现类 - * @author looly,lzpeng + * @author looly, lzpeng * @since 5.5.2 */ public class ResourceClassLoader extends SecureClassLoader { private final Map resourceMap; - /** * 缓存已经加载的类 */ - private final Map cacheClassMap; + private final Map> cacheClassMap; /** * 构造 @@ -34,36 +32,42 @@ public class ResourceClassLoader extends SecureClassLoader { public ResourceClassLoader(ClassLoader parentClassLoader, Map resourceMap) { super(ObjectUtil.defaultIfNull(parentClassLoader, ClassLoaderUtil.getClassLoader())); this.resourceMap = ObjectUtil.defaultIfNull(resourceMap, new HashMap<>()); - this.cacheClassMap = new ConcurrentHashMap<>(); + this.cacheClassMap = new HashMap<>(); } /** * 增加需要加载的类资源 + * * @param resource 资源,可以是文件、流或者字符串 * @return this */ - public ResourceClassLoader addResource(T resource){ + public ResourceClassLoader addResource(T resource) { this.resourceMap.put(resource.getName(), resource); return this; } @Override protected Class findClass(String name) throws ClassNotFoundException { - final Class clazz = cacheClassMap.computeIfAbsent(name, className -> { - final Resource resource = resourceMap.get(name); - if (null != resource) { - final byte[] bytes = resource.readBytes(); - return defineClass(name, bytes, 0, bytes.length); - } - try { - return super.findClass(name); - } catch (ClassNotFoundException e) { - return null; - } - }); + final Class clazz = cacheClassMap.computeIfAbsent(name, this::defineByName); if (clazz == null) { - throw new ClassNotFoundException(name); + return super.findClass(name); } return clazz; } + + /** + * 从给定资源中读取class的二进制流,然后生成类
+ * 如果这个类资源不存在,返回{@code null} + * + * @param name 类名 + * @return 定义的类 + */ + private Class defineByName(String name) { + final Resource resource = resourceMap.get(name); + if (null != resource) { + final byte[] bytes = resource.readBytes(); + return defineClass(name, bytes, 0, bytes.length); + } + return null; + } } From 851a10d3b0c93ebe10a152e944c949578a05f230 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 19 Nov 2021 01:30:08 +0800 Subject: [PATCH 6/8] add CipherWrapper --- CHANGELOG.md | 1 + .../java/cn/hutool/crypto/CipherWrapper.java | 121 ++++++++++++++++++ .../crypto/asymmetric/AsymmetricCrypto.java | 51 ++++---- .../crypto/symmetric/SymmetricCrypto.java | 51 ++++---- 4 files changed, 174 insertions(+), 50 deletions(-) create mode 100644 hutool-crypto/src/main/java/cn/hutool/crypto/CipherWrapper.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 84418d6f5..b666f7308 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * 【core 】 增加DefaultCloneable(pr#459@Gitee) * 【core 】 CollStreamUtil增加是否并行的重载(pr#467@Gitee) * 【core 】 ResourceClassLoader增加缓存(pr#1959@Github) +* 【crypto 】 增加CipherWrapper,增加setRandom(pr#1959@Github) * ### 🐞Bug修复 * 【core 】 修复FileResource构造fileName参数无效问题(issue#1942@Github) diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/CipherWrapper.java b/hutool-crypto/src/main/java/cn/hutool/crypto/CipherWrapper.java new file mode 100644 index 000000000..bb9a2578a --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/CipherWrapper.java @@ -0,0 +1,121 @@ +package cn.hutool.crypto; + +import javax.crypto.Cipher; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +/** + * {@link Cipher}包装类,提供初始化模式等额外方法
+ * 包装之后可提供自定义或默认的: + *
    + *
  • {@link AlgorithmParameterSpec}
  • + *
  • {@link SecureRandom}
  • + *
+ * + * @author looly + * @since 5.7.17 + */ +public class CipherWrapper { + + private final Cipher cipher; + /** + * 算法参数 + */ + private AlgorithmParameterSpec params; + /** + * 随机数生成器,可自定义随机数种子 + */ + private SecureRandom random; + + /** + * 构造 + * + * @param algorithm 算法名称 + */ + public CipherWrapper(String algorithm) { + this(SecureUtil.createCipher(algorithm)); + } + + /** + * 构造 + * + * @param cipher {@link Cipher} + */ + public CipherWrapper(Cipher cipher) { + this.cipher = cipher; + } + + /** + * 获取{@link AlgorithmParameterSpec}
+ * 在某些算法中,需要特别的参数,例如在ECIES中,此处为IESParameterSpec + * + * @return {@link AlgorithmParameterSpec} + */ + public AlgorithmParameterSpec getParams() { + return this.params; + } + + /** + * 设置 {@link AlgorithmParameterSpec},通常用于加盐或偏移向量 + * + * @param params {@link AlgorithmParameterSpec} + * @return this + */ + public CipherWrapper setParams(AlgorithmParameterSpec params) { + this.params = params; + return this; + } + + /** + * 设置随机数生成器,可自定义随机数种子 + * + * @param random 随机数生成器,可自定义随机数种子 + * @return this + */ + public CipherWrapper setRandom(SecureRandom random) { + this.random = random; + return this; + } + + /** + * 获取被包装的{@link Cipher} + * + * @return {@link Cipher} + */ + public Cipher getCipher() { + return this.cipher; + } + + /** + * 初始化{@link Cipher}为加密或者解密模式 + * + * @param mode 模式,见{@link Cipher#ENCRYPT_MODE} 或 {@link Cipher#DECRYPT_MODE} + * @param key 密钥 + * @return this + * @throws InvalidKeyException 无效key + * @throws InvalidAlgorithmParameterException 无效算法 + */ + public CipherWrapper initMode(int mode, Key key) + throws InvalidKeyException, InvalidAlgorithmParameterException { + final Cipher cipher = this.cipher; + final AlgorithmParameterSpec params = this.params; + final SecureRandom random = this.random; + if (null != params) { + if (null != random) { + cipher.init(mode, key, params, random); + } else { + cipher.init(mode, key, params); + } + } else { + if (null != random) { + cipher.init(mode, key, random); + } else { + cipher.init(mode, key); + } + } + return this; + } +} diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java index 22198e58a..25f5661c4 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/AsymmetricCrypto.java @@ -2,6 +2,7 @@ package cn.hutool.crypto.asymmetric; import cn.hutool.core.codec.Base64; import cn.hutool.core.io.FastByteArrayOutputStream; +import cn.hutool.crypto.CipherWrapper; import cn.hutool.crypto.CryptoException; import cn.hutool.crypto.KeyUtil; import cn.hutool.crypto.SecureUtil; @@ -16,6 +17,7 @@ import java.security.InvalidKeyException; import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; /** @@ -37,7 +39,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto /** * Cipher负责完成加密或解密工作 */ - protected Cipher cipher; + protected CipherWrapper cipherWrapper; /** * 加密的块大小 @@ -47,12 +49,6 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * 解密的块大小 */ protected int decryptBlockSize = -1; - - /** - * 算法参数 - */ - private AlgorithmParameterSpec algorithmParameterSpec; - // ------------------------------------------------------------------ Constructor start /** @@ -201,7 +197,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 5.4.3 */ public AlgorithmParameterSpec getAlgorithmParameterSpec() { - return algorithmParameterSpec; + return this.cipherWrapper.getParams(); } /** @@ -212,7 +208,19 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 5.4.3 */ public void setAlgorithmParameterSpec(AlgorithmParameterSpec algorithmParameterSpec) { - this.algorithmParameterSpec = algorithmParameterSpec; + this.cipherWrapper.setParams(algorithmParameterSpec); + } + + /** + * 设置随机数生成器,可自定义随机数种子 + * + * @param random 随机数生成器,可自定义随机数种子 + * @return this + * @since 5.7.17 + */ + public AsymmetricCrypto setRandom(SecureRandom random) { + this.cipherWrapper.setRandom(random); + return this; } @Override @@ -229,11 +237,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto final Key key = getKeyByType(keyType); lock.lock(); try { - initMode(Cipher.ENCRYPT_MODE, key); + final Cipher cipher = initMode(Cipher.ENCRYPT_MODE, key); if (this.encryptBlockSize < 0) { // 在引入BC库情况下,自动获取块大小 - final int blockSize = this.cipher.getBlockSize(); + final int blockSize = cipher.getBlockSize(); if (blockSize > 0) { this.encryptBlockSize = blockSize; } @@ -254,11 +262,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto final Key key = getKeyByType(keyType); lock.lock(); try { - initMode(Cipher.DECRYPT_MODE, key); + final Cipher cipher = initMode(Cipher.DECRYPT_MODE, key); if (this.decryptBlockSize < 0) { // 在引入BC库情况下,自动获取块大小 - final int blockSize = this.cipher.getBlockSize(); + final int blockSize = cipher.getBlockSize(); if (blockSize > 0) { this.decryptBlockSize = blockSize; } @@ -281,7 +289,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 5.4.3 */ public Cipher getCipher() { - return cipher; + return this.cipherWrapper.getCipher(); } /** @@ -290,7 +298,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * @since 4.5.2 */ protected void initCipher() { - this.cipher = SecureUtil.createCipher(algorithm); + this.cipherWrapper = new CipherWrapper(this.algorithm); } /** @@ -309,7 +317,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto // 不足分段 if (dataLength <= maxBlockSize) { - return this.cipher.doFinal(data, 0, dataLength); + return getCipher().doFinal(data, 0, dataLength); } // 分段解密 @@ -337,7 +345,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto // 对数据分段处理 while (remainLength > 0) { blockSize = Math.min(remainLength, maxBlockSize); - out.write(cipher.doFinal(data, offSet, blockSize)); + out.write(getCipher().doFinal(data, offSet, blockSize)); offSet += blockSize; remainLength = dataLength - offSet; @@ -351,14 +359,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto * * @param mode 模式,可选{@link Cipher#ENCRYPT_MODE}或者{@link Cipher#DECRYPT_MODE} * @param key 密钥 + * @return {@link Cipher} * @throws InvalidAlgorithmParameterException 异常算法错误 * @throws InvalidKeyException 异常KEY错误 */ - private void initMode(int mode, Key key) throws InvalidAlgorithmParameterException, InvalidKeyException { - if (null != this.algorithmParameterSpec) { - cipher.init(mode, key, this.algorithmParameterSpec); - } else { - cipher.init(mode, key); - } + private Cipher initMode(int mode, Key key) throws InvalidAlgorithmParameterException, InvalidKeyException { + return this.cipherWrapper.initMode(mode, key).getCipher(); } } diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java index ba92ab553..ea4a7756d 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/symmetric/SymmetricCrypto.java @@ -3,15 +3,16 @@ package cn.hutool.crypto.symmetric; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Opt; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.HexUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.CipherMode; +import cn.hutool.crypto.CipherWrapper; import cn.hutool.crypto.CryptoException; import cn.hutool.crypto.KeyUtil; import cn.hutool.crypto.Padding; -import cn.hutool.crypto.SecureUtil; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; @@ -25,6 +26,7 @@ import java.io.OutputStream; import java.io.Serializable; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; +import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -40,18 +42,11 @@ import java.util.concurrent.locks.ReentrantLock; public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, Serializable { private static final long serialVersionUID = 1L; + private CipherWrapper cipherWrapper; /** * SecretKey 负责保存对称密钥 */ private SecretKey secretKey; - /** - * Cipher负责完成加密或解密工作 - */ - private Cipher cipher; - /** - * 加密解密参数 - */ - private AlgorithmParameterSpec params; /** * 是否0填充 */ @@ -152,7 +147,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, this.isZeroPadding = true; } - this.cipher = SecureUtil.createCipher(algorithm); + this.cipherWrapper = new CipherWrapper(algorithm); return this; } @@ -171,7 +166,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 加密或解密 */ public Cipher getCipher() { - return cipher; + return cipherWrapper.getCipher(); } /** @@ -181,7 +176,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 自身 */ public SymmetricCrypto setParams(AlgorithmParameterSpec params) { - this.params = params; + this.cipherWrapper.setParams(params); return this; } @@ -192,8 +187,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 自身 */ public SymmetricCrypto setIv(IvParameterSpec iv) { - setParams(iv); - return this; + return setParams(iv); } /** @@ -203,7 +197,18 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @return 自身 */ public SymmetricCrypto setIv(byte[] iv) { - setIv(new IvParameterSpec(iv)); + return setIv(new IvParameterSpec(iv)); + } + + /** + * 设置随机数生成器,可自定义随机数种子 + * + * @param random 随机数生成器,可自定义随机数种子 + * @return this + * @since 5.7.17 + */ + public SymmetricCrypto setRandom(SecureRandom random){ + this.cipherWrapper.setRandom(random); return this; } @@ -237,6 +242,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @since 5.6.8 */ public byte[] update(byte[] data) { + final Cipher cipher = cipherWrapper.getCipher(); lock.lock(); try { return cipher.update(paddingDataWithZero(data, cipher.getBlockSize())); @@ -376,11 +382,8 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, */ private SymmetricCrypto initParams(String algorithm, AlgorithmParameterSpec paramsSpec) { if (null == paramsSpec) { - byte[] iv = null; - final Cipher cipher = this.cipher; - if (null != cipher) { - iv = cipher.getIV(); - } + byte[] iv = Opt.ofNullable(cipherWrapper) + .map(CipherWrapper::getCipher).map(Cipher::getIV).get(); // 随机IV if (StrUtil.startWithIgnoreCase(algorithm, "PBE")) { @@ -409,13 +412,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, * @throws InvalidAlgorithmParameterException 无效算法 */ private Cipher initMode(int mode) throws InvalidKeyException, InvalidAlgorithmParameterException { - final Cipher cipher = this.cipher; - if (null == this.params) { - cipher.init(mode, secretKey); - } else { - cipher.init(mode, secretKey, params); - } - return cipher; + return this.cipherWrapper.initMode(mode, this.secretKey).getCipher(); } /** From 0694891f5b1663e1684fbf5a1014d1bae2911e7e Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 19 Nov 2021 01:31:00 +0800 Subject: [PATCH 7/8] add CipherWrapper --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b666f7308..468ad037b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ * 【core 】 增加DefaultCloneable(pr#459@Gitee) * 【core 】 CollStreamUtil增加是否并行的重载(pr#467@Gitee) * 【core 】 ResourceClassLoader增加缓存(pr#1959@Github) -* 【crypto 】 增加CipherWrapper,增加setRandom(pr#1959@Github) +* 【crypto 】 增加CipherWrapper,增加setRandom(issue#1958@Github) * ### 🐞Bug修复 * 【core 】 修复FileResource构造fileName参数无效问题(issue#1942@Github) From 1efa09d8ad4bd6965a4b762f14fa236be8d7ee04 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 19 Nov 2021 02:38:12 +0800 Subject: [PATCH 8/8] fix bug --- CHANGELOG.md | 1 + .../java/cn/hutool/cache/impl/CacheObj.java | 4 +- .../java/cn/hutool/cache/impl/WeakCache.java | 111 +++++++++++++++++- .../java/cn/hutool/cache/WeakCacheTest.java | 21 ++++ 4 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 468ad037b..1c1df9dd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ * ### 🐞Bug修复 * 【core 】 修复FileResource构造fileName参数无效问题(issue#1942@Github) +* 【cache 】 修复WeakCache键值强关联导致的无法回收问题(issue#1953@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java index f9dca42a0..dc203cb9c 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java @@ -17,11 +17,11 @@ public class CacheObj implements Serializable{ protected final V obj; /** 上次访问时间 */ - private volatile long lastAccess; + protected volatile long lastAccess; /** 访问次数 */ protected AtomicLong accessCount = new AtomicLong(); /** 对象存活时长,0表示永久存活*/ - private final long ttl; + protected final long ttl; /** * 构造 diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java index 3a98bc601..21b2bb16a 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java @@ -1,5 +1,10 @@ package cn.hutool.cache.impl; +import cn.hutool.cache.Cache; +import cn.hutool.core.lang.func.Func0; +import cn.hutool.core.lang.mutable.MutableObj; + +import java.util.Iterator; import java.util.WeakHashMap; /** @@ -7,18 +12,112 @@ import java.util.WeakHashMap; * 对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。
* 丢弃某个键时,其条目从映射中有效地移除。
* + * @param 键类型 + * @param 值类型 * @author Looly - * - * @param 键 - * @param 值 - * @author looly * @since 3.0.7 */ -public class WeakCache extends TimedCache{ +public class WeakCache implements Cache { private static final long serialVersionUID = 1L; + TimedCache, V> timedCache; + + /** + * 构造 + * + * @param timeout 超时 + */ public WeakCache(long timeout) { - super(timeout, new WeakHashMap<>()); + this.timedCache = new TimedCache<>(timeout, new WeakHashMap<>()); } + @Override + public int capacity() { + return timedCache.capacity(); + } + + @Override + public long timeout() { + return timedCache.timeout(); + } + + @Override + public void put(K key, V object) { + timedCache.put(new MutableObj<>(key), object); + } + + @Override + public void put(K key, V object, long timeout) { + timedCache.put(new MutableObj<>(key), object, timeout); + } + + @Override + public V get(K key, boolean isUpdateLastAccess, Func0 supplier) { + return timedCache.get(new MutableObj<>(key), isUpdateLastAccess, supplier); + } + + @Override + public V get(K key, boolean isUpdateLastAccess) { + return timedCache.get(new MutableObj<>(key), isUpdateLastAccess); + } + + @Override + public Iterator> cacheObjIterator() { + final Iterator, V>> timedIter = timedCache.cacheObjIterator(); + return new Iterator>() { + @Override + public boolean hasNext() { + return timedIter.hasNext(); + } + + @Override + public CacheObj next() { + final CacheObj, V> next = timedIter.next(); + final CacheObj nextNew = new CacheObj<>(next.key.get(), next.obj, next.ttl); + nextNew.lastAccess = next.lastAccess; + nextNew.accessCount = next.accessCount; + return nextNew; + } + }; + } + + @Override + public int prune() { + return timedCache.prune(); + } + + @Override + public boolean isFull() { + return timedCache.isFull(); + } + + @Override + public void remove(K key) { + timedCache.remove(new MutableObj<>(key)); + } + + @Override + public void clear() { + timedCache.clear(); + } + + @Override + public int size() { + return timedCache.size(); + } + + @Override + public boolean isEmpty() { + return timedCache.isEmpty(); + } + + @Override + public boolean containsKey(K key) { + return timedCache.containsKey(new MutableObj<>(key)); + } + + @Override + public Iterator iterator() { + return timedCache.iterator(); + } } diff --git a/hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java b/hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java new file mode 100644 index 000000000..ce7b03dd8 --- /dev/null +++ b/hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java @@ -0,0 +1,21 @@ +package cn.hutool.cache; + +import cn.hutool.cache.impl.WeakCache; +import org.junit.Assert; +import org.junit.Test; + +public class WeakCacheTest { + + @Test + public void removeTest(){ + final WeakCache cache = new WeakCache<>(-1); + cache.put("abc", "123"); + cache.put("def", "456"); + + Assert.assertEquals(2, cache.size()); + + cache.remove("abc"); + + Assert.assertEquals(1, cache.size()); + } +}