diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2136dba910841a6bc31ed01538ac104485608a93..e7434e9d6b5e86e46ec7e6aa15012d7000d156f9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,35 @@
# 🚀Changelog
+-------------------------------------------------------------------------------------------------------------
+# 5.7.23 (2022-03-15)
+
+### 🐣新特性
+* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
+* 【core 】 AnnotationUtil增加getAnnotationAlias方法(pr#554@Gitee)
+* 【core 】 FileUtil.extName增加对tar.gz特殊处理(issue#I4W5FS@Gitee)
+* 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee)
+* 【core 】 增加Table实现(issue#2179@Github)
+* 【core 】 增加UniqueKeySet(issue#I4WUWR@Gitee)
+* 【core 】 阿拉伯数字转换成中文对发票票面金额转换的扩展(pr#570@Gitee)
+* 【core 】 ArrayUtil增加replace方法(pr#570@Gitee)
+* 【core 】 CsvReadConfig增加自定义标题行行号(issue#2180@Github)
+* 【db 】 增加MongoDB4.x支持(pr#568@Gitee)
+*
+### 🐞Bug修复
+* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
+* 【core 】 修复NumberConverter对数字转换的问题(issue#I4WPF4@Gitee)
+* 【core 】 修复ReflectUtil.getMethods获取接口方法问题(issue#I4WUWR@Gitee)
+* 【core 】 修复NamingCase中大写转换问题(pr#572@Gitee)
+* 【http 】 修复GET重定向时,携带参数问题(issue#2189@Github)
+* 【core 】 修复FileUtil、FileCopier相对路径获取父路径错误问题(pr#2188@Github)
+
-------------------------------------------------------------------------------------------------------------
# 5.7.22 (2022-03-01)
### 🐣新特性
-* 【poi 】 ExcelUtil.readBySax增加对POI-5.2.0的兼容性(issue#I4TJF4@gitee)
-* 【extra 】 Ftp增加构造(issue#I4TKXP@gitee)
+* 【poi 】 ExcelUtil.readBySax增加对POI-5.2.0的兼容性(issue#I4TJF4@Gitee)
+* 【extra 】 Ftp增加构造(issue#I4TKXP@Gitee)
* 【core 】 GenericBuilder支持Map构建(pr#540@Github)
* 【json 】 新增TemporalAccessorSerializer
* 【core 】 使多个xxxBuilder实现Builder接口,扩展CheckedUtil(pr#545@Gitee)
@@ -538,7 +561,7 @@
* 【json 】 增加JSONWriter
* 【core 】 IdUtil增加getWorkerId和getDataCenterId(issueI3Y5NI@Gitee)
* 【core 】 JWTValidator增加leeway重载
-* 【core 】 增加RegexPool(issue#I3W9ZF@gitee)
+* 【core 】 增加RegexPool(issue#I3W9ZF@Gitee)
### 🐞Bug修复
* 【json 】 修复XML转义字符的问题(issue#I3XH09@Gitee)
diff --git a/README-EN.md b/README-EN.md
index 75fcfed1208d82e8f75cbaedcadae898540776c9..7456a08f77dce5d9eeff955d555405332096c860 100644
--- a/README-EN.md
+++ b/README-EN.md
@@ -111,6 +111,8 @@ Each module can be introduced individually, or all modules can be introduced by
[📘Chinese documentation](https://www.hutool.cn/docs/)
+[📘Chinese back-up documentation](https://plus.hutool.cn/docs/#/)
+
[📙API](https://apidoc.gitee.com/dromara/hutool/)
[🎬Video](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2)
@@ -142,18 +144,18 @@ We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop:
cn.hutoolhutool-all
- 5.7.22
+ 5.7.23
```
### 🍐Gradle
```
-implementation 'cn.hutool:hutool-all:5.7.22'
+implementation 'cn.hutool:hutool-all:5.7.23'
```
## 📥Download
-- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.22/)
+- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.23/)
> 🔔️note:
> Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.
diff --git a/README.md b/README.md
index 4516e5e0de10eed8c75e1d9402430be6ae10fb16..409e4e22d9bc15b7458dca3ec5787fc65659709a 100644
--- a/README.md
+++ b/README.md
@@ -107,6 +107,8 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
[📘中文文档](https://www.hutool.cn/docs/)
+[📘中文备用文档](https://plus.hutool.cn/docs/#/)
+
[📙参考API](https://apidoc.gitee.com/dromara/hutool/)
[🎬视频介绍](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2)
@@ -142,20 +144,20 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
cn.hutoolhutool-all
- 5.7.22
+ 5.7.23
```
### 🍐Gradle
```
-implementation 'cn.hutool:hutool-all:5.7.22'
+implementation 'cn.hutool:hutool-all:5.7.23'
```
### 📥下载jar
点击以下链接,下载`hutool-all-X.X.X.jar`即可:
-- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.22/)
+- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.23/)
> 🔔️注意
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
diff --git a/bin/version.txt b/bin/version.txt
index e2ff09a282565afc2fd83d3a142516ba537cf703..a5c3e01d2dc7743e5dfdfb2cbb16fb0dcb57c6af 100755
--- a/bin/version.txt
+++ b/bin/version.txt
@@ -1 +1 @@
-5.7.22
+5.7.23
diff --git a/docs/js/version.js b/docs/js/version.js
index e637f5f205ffdcbffca68c58acfa77964334ab54..37b3ee602cac386a8c6cfbb55736ae3287c5b4be 100644
--- a/docs/js/version.js
+++ b/docs/js/version.js
@@ -1 +1 @@
-var version = '5.7.22'
\ No newline at end of file
+var version = '5.7.23'
\ No newline at end of file
diff --git a/hutool-all/pom.xml b/hutool-all/pom.xml
index a516862eee3ee79c635de1325e8437c5353d03c0..d99b05ea1d81b20941bb8707faa590873c78d9f7 100644
--- a/hutool-all/pom.xml
+++ b/hutool-all/pom.xml
@@ -9,7 +9,7 @@
cn.hutoolhutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOThutool-all
diff --git a/hutool-aop/pom.xml b/hutool-aop/pom.xml
index f9dc30ef0406c4e161d1a0777545e4bfafdb3a15..9a53b9232e528afe6baad27bebe7e93b4356f345 100644
--- a/hutool-aop/pom.xml
+++ b/hutool-aop/pom.xml
@@ -9,7 +9,7 @@
cn.hutoolhutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOThutool-aop
diff --git a/hutool-bloomFilter/pom.xml b/hutool-bloomFilter/pom.xml
index 4abc55f45a19c2ebbcc3378925233e3ec6378ae7..6820aa9968f5aeebaa182172286238084b311138 100644
--- a/hutool-bloomFilter/pom.xml
+++ b/hutool-bloomFilter/pom.xml
@@ -9,7 +9,7 @@
cn.hutoolhutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOThutool-bloomFilter
diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml
index 3490b7ceeda31d2bc4dca473873ac2ae6c135198..35c3ab9aa97c1e458ddeef5c23d6e559903f13d6 100644
--- a/hutool-bom/pom.xml
+++ b/hutool-bom/pom.xml
@@ -9,7 +9,7 @@
cn.hutoolhutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOThutool-bom
diff --git a/hutool-cache/pom.xml b/hutool-cache/pom.xml
index 298a97a6c2a722d4651f4675b41ffe55c1871e57..430d0b0925166f1adaea23f3fa1cc7a3917782c0 100644
--- a/hutool-cache/pom.xml
+++ b/hutool-cache/pom.xml
@@ -9,7 +9,7 @@
cn.hutoolhutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOThutool-cache
diff --git a/hutool-captcha/pom.xml b/hutool-captcha/pom.xml
index 17564bff02b89fcbec1766fffe98240a6ee35a20..b2dbcf27fb2cf1556b672b12226ce0a4acd9144e 100644
--- a/hutool-captcha/pom.xml
+++ b/hutool-captcha/pom.xml
@@ -9,7 +9,7 @@
cn.hutoolhutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOThutool-captcha
diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml
index 9c225e9c7884942f289052cd9030546944c714b0..6ca1dd789492960738e28d87a4ba192caa635c46 100644
--- a/hutool-core/pom.xml
+++ b/hutool-core/pom.xml
@@ -9,7 +9,7 @@
cn.hutoolhutool-parent
- 5.7.22
+ 5.7.23-SNAPSHOThutool-core
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java
new file mode 100644
index 0000000000000000000000000000000000000000..bcf2ba4b5829e8b74054c3ee227c443c4ff73389
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationProxy.java
@@ -0,0 +1,88 @@
+package cn.hutool.core.annotation;
+
+import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.core.util.StrUtil;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 注解代理
+ * 通过代理指定注解,可以自定义调用注解的方法逻辑,如支持{@link Alias} 注解
+ *
+ * @param 注解类型
+ * @since 5.7.23
+ */
+public class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final T annotation;
+ private final Class type;
+ private final Map attributes;
+
+ /**
+ * 构造
+ *
+ * @param annotation 注解
+ */
+ public AnnotationProxy(T annotation) {
+ this.annotation = annotation;
+ //noinspection unchecked
+ this.type = (Class) annotation.annotationType();
+ this.attributes = initAttributes();
+ }
+
+
+ @Override
+ public Class extends Annotation> annotationType() {
+ return type;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+
+ // 注解别名
+ Alias alias = method.getAnnotation(Alias.class);
+ if(null != alias){
+ final String name = alias.value();
+ if(StrUtil.isNotBlank(name)){
+ if(false == attributes.containsKey(name)){
+ throw new IllegalArgumentException(StrUtil.format("No method for alias: [{}]", name));
+ }
+ return attributes.get(name);
+ }
+ }
+
+ final Object value = attributes.get(method.getName());
+ if (value != null) {
+ return value;
+ }
+ return method.invoke(this, args);
+ }
+
+ /**
+ * 初始化注解的属性
+ * 此方法预先调用所有注解的方法,将注解方法值缓存于attributes中
+ *
+ * @return 属性(方法结果)映射
+ */
+ private Map initAttributes() {
+ final Method[] methods = ReflectUtil.getMethods(this.type);
+ final Map attributes = new HashMap<>(methods.length, 1);
+
+ for (Method method : methods) {
+ // 跳过匿名内部类自动生成的方法
+ if (method.isSynthetic()) {
+ continue;
+ }
+
+ attributes.put(method.getName(), ReflectUtil.invoke(this.annotation, method));
+ }
+
+ return attributes;
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
index 1049a517ae7ef2fec7a685e58bb60ae79d83856c..be44f080a59adf921fccd1ff6cd796a7ad6df39f 100644
--- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
@@ -42,7 +42,7 @@ public class AnnotationUtil {
/**
* 获取指定注解
*
- * @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
+ * @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param isToCombination 是否为转换为组合注解
* @return 注解对象
*/
@@ -205,9 +205,9 @@ public class AnnotationUtil {
/**
* 设置新的注解的属性(字段)值
*
- * @param annotation 注解对象
+ * @param annotation 注解对象
* @param annotationField 注解属性(字段)名称
- * @param value 要更新的属性值
+ * @param value 要更新的属性值
* @since 5.5.2
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@@ -215,4 +215,19 @@ public class AnnotationUtil {
final Map memberValues = (Map) ReflectUtil.getFieldValue(Proxy.getInvocationHandler(annotation), "memberValues");
memberValues.put(annotationField, value);
}
+
+ /**
+ * 获取别名支持后的注解
+ *
+ * @param annotationEle 被注解的类
+ * @param annotationType 注解类型Class
+ * @param 注解类型
+ * @return 别名支持后的注解
+ * @since 5.7.23
+ */
+ @SuppressWarnings("unchecked")
+ public static T getAnnotationAlias(AnnotatedElement annotationEle, Class annotationType) {
+ final T annotation = getAnnotation(annotationEle, annotationType);
+ return (T) Proxy.newProxyInstance(annotationType.getClassLoader(), new Class[]{annotationType}, new AnnotationProxy<>(annotation));
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
index b66b15f0df1936a1a2412879fef011196edc374f..84e7c59178f32c2605c37fa07a293bfe0d5d9442 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
@@ -225,8 +225,8 @@ public class BeanUtil {
*/
private static Map internalGetPropertyDescriptorMap(Class> clazz, boolean ignoreCase) throws BeanException {
final PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz);
- final Map map = ignoreCase ? new CaseInsensitiveMap<>(propertyDescriptors.length, 1)
- : new HashMap<>((int) (propertyDescriptors.length), 1);
+ final Map map = ignoreCase ? new CaseInsensitiveMap<>(propertyDescriptors.length, 1f)
+ : new HashMap<>(propertyDescriptors.length, 1);
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
map.put(propertyDescriptor.getName(), propertyDescriptor);
diff --git a/hutool-core/src/main/java/cn/hutool/core/builder/EqualsBuilder.java b/hutool-core/src/main/java/cn/hutool/core/builder/EqualsBuilder.java
index bb7c7e46cd2fe188b70de5e856860febbd8597b3..e68ccdf3872d8733790aa9fb50f804655eb61d3b 100644
--- a/hutool-core/src/main/java/cn/hutool/core/builder/EqualsBuilder.java
+++ b/hutool-core/src/main/java/cn/hutool/core/builder/EqualsBuilder.java
@@ -70,7 +70,7 @@ public class EqualsBuilder implements Builder {
* Converters value pair into a register pair.
*
*
- * @param lhs this object
+ * @param lhs {@code this} object
* @param rhs the other object
* @return the pair
*/
@@ -82,15 +82,15 @@ public class EqualsBuilder implements Builder {
/**
*
- * Returns true if the registry contains the given object pair.
+ * Returns {@code true} if the registry contains the given object pair.
* Used by the reflection methods to avoid infinite loops.
* Objects might be swapped therefore a check is needed if the object pair
* is registered in given or swapped order.
*
*
- * @param lhs this object to lookup in registry
+ * @param lhs {@code this} object to lookup in registry
* @param rhs the other object to lookup on registry
- * @return boolean true if the registry contains the given object.
+ * @return boolean {@code true} if the registry contains the given object.
* @since 3.0
*/
static boolean isRegistered(final Object lhs, final Object rhs) {
@@ -108,7 +108,7 @@ public class EqualsBuilder implements Builder {
* Used by the reflection methods to avoid infinite loops.
*
*
- * @param lhs this object to register
+ * @param lhs {@code this} object to register
* @param rhs the other object to register
*/
static void register(final Object lhs, final Object rhs) {
@@ -131,7 +131,7 @@ public class EqualsBuilder implements Builder {
*
* Used by the reflection methods to avoid infinite loops.
*
- * @param lhs this object to unregister
+ * @param lhs {@code this} object to unregister
* @param rhs the other object to unregister
* @since 3.0
*/
@@ -170,7 +170,7 @@ public class EqualsBuilder implements Builder {
* @param lhs 此对象
* @param rhs 另一个对象
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
- * @return 两个对象是否equals,是返回true
+ * @return 两个对象是否equals,是返回{@code true}
*/
public static boolean reflectionEquals(final Object lhs, final Object rhs, final Collection excludeFields) {
return reflectionEquals(lhs, rhs, ArrayUtil.toArray(excludeFields, String.class));
@@ -182,62 +182,62 @@ public class EqualsBuilder implements Builder {
* @param lhs 此对象
* @param rhs 另一个对象
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
- * @return 两个对象是否equals,是返回true
+ * @return 两个对象是否equals,是返回{@code true}
*/
public static boolean reflectionEquals(final Object lhs, final Object rhs, final String... excludeFields) {
return reflectionEquals(lhs, rhs, false, null, excludeFields);
}
/**
- *
This method uses reflection to determine if the two Objects
+ *
This method uses reflection to determine if the two {@code Object}s
* are equal.
*
- *
It uses AccessibleObject.setAccessible to gain access to private
+ *
It uses {@code AccessibleObject.setAccessible} to gain access to private
* fields. This means that it will throw a security exception if run under
* a security manager, if the permissions are not set up correctly. It is also
* not as efficient as testing explicitly. Non-primitive fields are compared using
- * equals().
+ * {@code equals()}.
*
- *
If the TestTransients parameter is set to true, transient
+ *
If the TestTransients parameter is set to {@code true}, transient
* members will be tested, otherwise they are ignored, as they are likely
- * derived fields, and not part of the value of the Object.
+ * derived fields, and not part of the value of the {@code Object}.
*
*
Static fields will not be tested. Superclass fields will be included.
*
- * @param lhs this object
+ * @param lhs {@code this} object
* @param rhs the other object
* @param testTransients whether to include transient fields
- * @return true if the two Objects have tested equals.
+ * @return {@code true} if the two Objects have tested equals.
*/
public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients) {
return reflectionEquals(lhs, rhs, testTransients, null);
}
/**
- *
This method uses reflection to determine if the two Objects
+ *
This method uses reflection to determine if the two {@code Object}s
* are equal.
*
- *
It uses AccessibleObject.setAccessible to gain access to private
+ *
It uses {@code AccessibleObject.setAccessible} to gain access to private
* fields. This means that it will throw a security exception if run under
* a security manager, if the permissions are not set up correctly. It is also
* not as efficient as testing explicitly. Non-primitive fields are compared using
- * equals().
+ * {@code equals()}.
*
- *
If the testTransients parameter is set to true, transient
+ *
If the testTransients parameter is set to {@code true}, transient
* members will be tested, otherwise they are ignored, as they are likely
- * derived fields, and not part of the value of the Object.
+ * derived fields, and not part of the value of the {@code Object}.
*
*
Static fields will not be included. Superclass fields will be appended
* up to and including the specified superclass. A null superclass is treated
* as java.lang.Object.
*
- * @param lhs this object
+ * @param lhs {@code this} object
* @param rhs the other object
* @param testTransients whether to include transient fields
* @param reflectUpToClass the superclass to reflect up to (inclusive),
- * may be null
+ * may be {@code null}
* @param excludeFields array of field names to exclude from testing
- * @return true if the two Objects have tested equals.
+ * @return {@code true} if the two Objects have tested equals.
* @since 2.0
*/
public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients, final Class> reflectUpToClass,
@@ -343,9 +343,9 @@ public class EqualsBuilder implements Builder {
//-------------------------------------------------------------------------
/**
- *
Adds the result of super.equals() to this builder.
+ *
Adds the result of {@code super.equals()} to this builder.
*
- * @param superEquals the result of calling super.equals()
+ * @param superEquals the result of calling {@code super.equals()}
* @return EqualsBuilder - used to chain calls.
* @since 2.0
*/
@@ -360,8 +360,8 @@ public class EqualsBuilder implements Builder {
//-------------------------------------------------------------------------
/**
- *
Test if two Objects are equal using their
- * equals method.
+ *
Test if two {@code Object}s are equal using their
+ * {@code equals} method.
*
* @param lhs the left hand object
* @param rhs the right hand object
@@ -388,11 +388,11 @@ public class EqualsBuilder implements Builder {
/**
*
- * Test if two long s are equal.
+ * Test if two {@code long} s are equal.
*
*
- * @param lhs the left hand long
- * @param rhs the right hand long
+ * @param lhs the left hand {@code long}
+ * @param rhs the right hand {@code long}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final long lhs, final long rhs) {
@@ -404,10 +404,10 @@ public class EqualsBuilder implements Builder {
}
/**
- *
Test if two ints are equal.
+ *
Test if two {@code int}s are equal.
*
- * @param lhs the left hand int
- * @param rhs the right hand int
+ * @param lhs the left hand {@code int}
+ * @param rhs the right hand {@code int}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final int lhs, final int rhs) {
@@ -419,10 +419,10 @@ public class EqualsBuilder implements Builder {
}
/**
- *
Test if two shorts are equal.
+ *
Test if two {@code short}s are equal.
*
- * @param lhs the left hand short
- * @param rhs the right hand short
+ * @param lhs the left hand {@code short}
+ * @param rhs the right hand {@code short}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final short lhs, final short rhs) {
@@ -434,10 +434,10 @@ public class EqualsBuilder implements Builder {
}
/**
- *
Test if two chars are equal.
+ *
Test if two {@code char}s are equal.
*
- * @param lhs the left hand char
- * @param rhs the right hand char
+ * @param lhs the left hand {@code char}
+ * @param rhs the right hand {@code char}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final char lhs, final char rhs) {
@@ -449,10 +449,10 @@ public class EqualsBuilder implements Builder {
}
/**
- *
Test if two bytes are equal.
+ *
Test if two {@code byte}s are equal.
*
- * @param lhs the left hand byte
- * @param rhs the right hand byte
+ * @param lhs the left hand {@code byte}
+ * @param rhs the right hand {@code byte}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final byte lhs, final byte rhs) {
@@ -464,16 +464,16 @@ public class EqualsBuilder implements Builder {
}
/**
- *
Test if two doubles are equal by testing that the
- * pattern of bits returned by doubleToLong are equal.
+ *
Test if two {@code double}s are equal by testing that the
+ * pattern of bits returned by {@code doubleToLong} are equal.
*
- *
This handles NaNs, Infinities, and -0.0.
+ *
This handles NaNs, Infinities, and {@code -0.0}.
*
*
It is compatible with the hash code generated by
- * HashCodeBuilder.
+ * {@code HashCodeBuilder}.
*
- * @param lhs the left hand double
- * @param rhs the right hand double
+ * @param lhs the left hand {@code double}
+ * @param rhs the right hand {@code double}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final double lhs, final double rhs) {
@@ -484,16 +484,16 @@ public class EqualsBuilder implements Builder {
}
/**
- *
Test if two floats are equal byt testing that the
+ *
Test if two {@code float}s are equal byt testing that the
* pattern of bits returned by doubleToLong are equal.
*
- *
This handles NaNs, Infinities, and -0.0.
+ *
This handles NaNs, Infinities, and {@code -0.0}.
*
*
It is compatible with the hash code generated by
- * HashCodeBuilder.
+ * {@code HashCodeBuilder}.
*
- * @param lhs the left hand float
- * @param rhs the right hand float
+ * @param lhs the left hand {@code float}
+ * @param rhs the right hand {@code float}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final float lhs, final float rhs) {
@@ -504,10 +504,10 @@ public class EqualsBuilder implements Builder {
}
/**
- *
Test if two booleanss are equal.
+ *
Test if two {@code booleans}s are equal.
*
- * @param lhs the left hand boolean
- * @param rhs the right hand boolean
+ * @param lhs the left hand {@code boolean}
+ * @param rhs the right hand {@code boolean}
* @return EqualsBuilder - used to chain calls.
*/
public EqualsBuilder append(final boolean lhs, final boolean rhs) {
@@ -519,7 +519,7 @@ public class EqualsBuilder implements Builder {
}
/**
- *
Returns true if the fields that have been checked
+ *
Returns {@code true} if the fields that have been checked
* are all equal.
*
* @return boolean
@@ -529,11 +529,11 @@ public class EqualsBuilder implements Builder {
}
/**
- *
Returns true if the fields that have been checked
+ *
Returns {@code true} if the fields that have been checked
* are all equal.
*
- * @return true if all of the fields that have been checked
- * are equal, false otherwise.
+ * @return {@code true} if all of the fields that have been checked
+ * are equal, {@code false} otherwise.
* @since 3.0
*/
@Override
@@ -542,7 +542,7 @@ public class EqualsBuilder implements Builder {
}
/**
- * Sets the isEquals value.
+ * Sets the {@code isEquals} value.
*
* @param isEquals The value to set.
* @return this
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
index dd22d5275b4b1ddc1d190879502ca20f0b836c41..21120cd252170e55614716adc8de0335e48b07a2 100644
--- a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java
@@ -1178,20 +1178,10 @@ public class CollUtil {
return collection;
}
- Collection collection2 = ObjectUtil.clone(collection);
- if (null == collection2) {
- // 不支持clone
- collection2 = create(collection.getClass());
- }
- if (isEmpty(collection2)) {
+ final Collection collection2 = create(collection.getClass());
+ if (isEmpty(collection)) {
return collection2;
}
- try {
- collection2.clear();
- } catch (UnsupportedOperationException e) {
- // 克隆后的对象不支持清空,说明为不可变集合对象,使用默认的ArrayList保存结果
- collection2 = new ArrayList<>();
- }
T modified;
for (T t : collection) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java
index f03451bc3284b33e612c8f03c510c68f0a9ce5a6..2634eefae298917b9f4a5dce793b368199dc2293 100644
--- a/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/IterUtil.java
@@ -906,4 +906,19 @@ public class IterUtil {
// 当两个Iterable长度不一致时返回false
return false == (it1.hasNext() || it2.hasNext());
}
+
+ /**
+ * 清空指定{@link Iterator},此方法遍历后调用{@link Iterator#remove()}移除每个元素
+ *
+ * @param iterator {@link Iterator}
+ * @since 5.7.23
+ */
+ public static void clear(Iterator> iterator) {
+ if (null != iterator) {
+ while (iterator.hasNext()) {
+ iterator.next();
+ iterator.remove();
+ }
+ }
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/UniqueKeySet.java b/hutool-core/src/main/java/cn/hutool/core/collection/UniqueKeySet.java
new file mode 100644
index 0000000000000000000000000000000000000000..f4fbcb15c809f0d253a0430001ca1dd7eb5e2bc5
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/UniqueKeySet.java
@@ -0,0 +1,152 @@
+package cn.hutool.core.collection;
+
+import cn.hutool.core.map.MapBuilder;
+import cn.hutool.core.util.ObjectUtil;
+
+import java.io.Serializable;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * 唯一键的Set
+ * 通过自定义唯一键,通过{@link #uniqueGenerator}生成节点对象对应的键作为Map的key,确定唯一
+ * 此Set与HashSet不同的是,HashSet依赖于{@link Object#equals(Object)}确定唯一
+ * 但是很多时候我们无法对对象进行修改,此时在外部定义一个唯一规则,即可完成去重。
+ *
+ * {@code Set set = new UniqueKeySet<>(UniqueTestBean::getId);}
+ *
+ *
+ * @param 唯一键类型
+ * @param 值对象
+ * @author looly
+ * @since 5.7.23
+ */
+public class UniqueKeySet extends AbstractSet implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private Map map;
+ private final Function uniqueGenerator;
+
+ //region 构造
+
+ /**
+ * 构造
+ *
+ * @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
+ */
+ public UniqueKeySet(Function uniqueGenerator) {
+ this(false, uniqueGenerator);
+ }
+
+ /**
+ * 构造
+ *
+ * @param isLinked 是否保持加入顺序
+ * @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
+ */
+ public UniqueKeySet(boolean isLinked, Function uniqueGenerator) {
+ this(MapBuilder.create(isLinked), uniqueGenerator);
+ }
+
+ /**
+ * 构造
+ *
+ * @param initialCapacity 初始容量
+ * @param loadFactor 增长因子
+ * @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
+ */
+ public UniqueKeySet(int initialCapacity, float loadFactor, Function uniqueGenerator) {
+ this(MapBuilder.create(new HashMap<>(initialCapacity, loadFactor)), uniqueGenerator);
+ }
+
+ /**
+ * 构造
+ *
+ * @param builder 初始Map,定义了Map类型
+ * @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
+ */
+ public UniqueKeySet(MapBuilder builder, Function uniqueGenerator) {
+ this.map = builder.build();
+ this.uniqueGenerator = uniqueGenerator;
+ }
+ //endregion
+
+ @Override
+ public Iterator iterator() {
+ return map.values().iterator();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ //noinspection unchecked
+ return map.containsKey(this.uniqueGenerator.apply((V) o));
+ }
+
+ @Override
+ public boolean add(V v) {
+ return null == map.put(this.uniqueGenerator.apply(v), v);
+ }
+
+ /**
+ * 加入值,如果值已经存在,则忽略之
+ *
+ * @param v 值
+ * @return 是否成功加入
+ */
+ public boolean addIfAbsent(V v) {
+ return null == map.putIfAbsent(this.uniqueGenerator.apply(v), v);
+ }
+
+ /**
+ * 加入集合中所有的值,如果值已经存在,则忽略之
+ *
+ * @param c 集合
+ * @return 是否有一个或多个被加入成功
+ */
+ public boolean addAllIfAbsent(Collection extends V> c) {
+ boolean modified = false;
+ for (V v : c)
+ if (addIfAbsent(v)) {
+ modified = true;
+ }
+ return modified;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ //noinspection unchecked
+ return null != map.remove(this.uniqueGenerator.apply((V) o));
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public UniqueKeySet clone() {
+ try {
+ UniqueKeySet newSet = (UniqueKeySet) super.clone();
+ newSet.map = ObjectUtil.clone(this.map);
+ return newSet;
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError(e);
+ }
+ }
+
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java b/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java
index 96b19c52f0d47f3773bba5fb6a92f36627e57007..bbc1fd347045c30cb275d14350dab26d3386052e 100644
--- a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java
+++ b/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java
@@ -77,6 +77,8 @@ import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.DoubleAdder;
+import java.util.concurrent.atomic.LongAdder;
/**
* 转换器登记中心
@@ -389,11 +391,13 @@ public class ConverterRegistry implements Serializable {
defaultConverterMap.put(Integer.class, new NumberConverter(Integer.class));
defaultConverterMap.put(AtomicInteger.class, new NumberConverter(AtomicInteger.class));// since 3.0.8
defaultConverterMap.put(Long.class, new NumberConverter(Long.class));
+ defaultConverterMap.put(LongAdder.class, new NumberConverter(LongAdder.class));
defaultConverterMap.put(AtomicLong.class, new NumberConverter(AtomicLong.class));// since 3.0.8
defaultConverterMap.put(Byte.class, new NumberConverter(Byte.class));
defaultConverterMap.put(Short.class, new NumberConverter(Short.class));
defaultConverterMap.put(Float.class, new NumberConverter(Float.class));
defaultConverterMap.put(Double.class, new NumberConverter(Double.class));
+ defaultConverterMap.put(DoubleAdder.class, new NumberConverter(DoubleAdder.class));
defaultConverterMap.put(Character.class, new CharacterConverter());
defaultConverterMap.put(Boolean.class, new BooleanConverter());
defaultConverterMap.put(AtomicBoolean.class, new AtomicBooleanConverter());// since 3.0.8
diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java
index d6da1d22b578944fd72f2ec6fe446061b9c1b1fb..6ec3f0e1601799a15bfceec6d898f2a6681258e3 100644
--- a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java
@@ -40,7 +40,6 @@ public class NumberChineseFormatter {
new ChineseUnit('亿', 1_0000_0000, true),
};
-
/**
* 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
*
@@ -53,15 +52,26 @@ public class NumberChineseFormatter {
}
/**
- * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
+ * 阿拉伯数字转换成中文.
+ *
+ *