mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
Merge remote-tracking branch 'origin/v5-dev' into v5-dev
# Conflicts: # hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java
This commit is contained in:
commit
88a9fc4a37
@ -2,14 +2,20 @@
|
|||||||
# 🚀Changelog
|
# 🚀Changelog
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
# 5.7.23 (2022-03-04)
|
# 5.7.23 (2022-03-09)
|
||||||
|
|
||||||
### 🐣新特性
|
### 🐣新特性
|
||||||
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
|
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
|
||||||
* 【core 】 AnnotationUtil增加getAnnotationAlias方法(pr#554@Gitee)
|
* 【core 】 AnnotationUtil增加getAnnotationAlias方法(pr#554@Gitee)
|
||||||
* 【core 】 FileUtil.extName增加对tar.gz特殊处理(issue#I4W5FS@Gitee)
|
* 【core 】 FileUtil.extName增加对tar.gz特殊处理(issue#I4W5FS@Gitee)
|
||||||
|
* 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee)
|
||||||
|
* 【core 】 增加Table实现(issue#2179@Github)
|
||||||
|
* 【core 】 增加UniqueKeySet(issue#I4WUWR@Gitee)
|
||||||
|
*
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
|
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
|
||||||
|
* 【core 】 修复NumberConverter对数字转换的问题(issue#I4WPF4@Gitee)
|
||||||
|
* 【core 】 修复ReflectUtil.getMethods获取接口方法问题(issue#I4WUWR@Gitee)
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
# 5.7.22 (2022-03-01)
|
# 5.7.22 (2022-03-01)
|
||||||
|
@ -39,7 +39,7 @@ public class AnnotationProxy<T extends Annotation> implements Annotation, Invoca
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<? extends Annotation> annotationType() {
|
public Class<? extends Annotation> annotationType() {
|
||||||
return null;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -225,8 +225,8 @@ public class BeanUtil {
|
|||||||
*/
|
*/
|
||||||
private static Map<String, PropertyDescriptor> internalGetPropertyDescriptorMap(Class<?> clazz, boolean ignoreCase) throws BeanException {
|
private static Map<String, PropertyDescriptor> internalGetPropertyDescriptorMap(Class<?> clazz, boolean ignoreCase) throws BeanException {
|
||||||
final PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz);
|
final PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz);
|
||||||
final Map<String, PropertyDescriptor> map = ignoreCase ? new CaseInsensitiveMap<>(propertyDescriptors.length, 1)
|
final Map<String, PropertyDescriptor> map = ignoreCase ? new CaseInsensitiveMap<>(propertyDescriptors.length, 1f)
|
||||||
: new HashMap<>((int) (propertyDescriptors.length), 1);
|
: new HashMap<>(propertyDescriptors.length, 1);
|
||||||
|
|
||||||
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
|
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
|
||||||
map.put(propertyDescriptor.getName(), propertyDescriptor);
|
map.put(propertyDescriptor.getName(), propertyDescriptor);
|
||||||
|
@ -70,7 +70,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
* Converters value pair into a register pair.
|
* Converters value pair into a register pair.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param lhs <code>this</code> object
|
* @param lhs {@code this} object
|
||||||
* @param rhs the other object
|
* @param rhs the other object
|
||||||
* @return the pair
|
* @return the pair
|
||||||
*/
|
*/
|
||||||
@ -82,15 +82,15 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Returns <code>true</code> 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.
|
* Used by the reflection methods to avoid infinite loops.
|
||||||
* Objects might be swapped therefore a check is needed if the object pair
|
* Objects might be swapped therefore a check is needed if the object pair
|
||||||
* is registered in given or swapped order.
|
* is registered in given or swapped order.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param lhs <code>this</code> object to lookup in registry
|
* @param lhs {@code this} object to lookup in registry
|
||||||
* @param rhs the other object to lookup on registry
|
* @param rhs the other object to lookup on registry
|
||||||
* @return boolean <code>true</code> if the registry contains the given object.
|
* @return boolean {@code true} if the registry contains the given object.
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
static boolean isRegistered(final Object lhs, final Object rhs) {
|
static boolean isRegistered(final Object lhs, final Object rhs) {
|
||||||
@ -108,7 +108,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
* Used by the reflection methods to avoid infinite loops.
|
* Used by the reflection methods to avoid infinite loops.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param lhs <code>this</code> object to register
|
* @param lhs {@code this} object to register
|
||||||
* @param rhs the other object to register
|
* @param rhs the other object to register
|
||||||
*/
|
*/
|
||||||
static void register(final Object lhs, final Object rhs) {
|
static void register(final Object lhs, final Object rhs) {
|
||||||
@ -131,7 +131,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
* <p>
|
* <p>
|
||||||
* Used by the reflection methods to avoid infinite loops.
|
* Used by the reflection methods to avoid infinite loops.
|
||||||
*
|
*
|
||||||
* @param lhs <code>this</code> object to unregister
|
* @param lhs {@code this} object to unregister
|
||||||
* @param rhs the other object to unregister
|
* @param rhs the other object to unregister
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
@ -170,7 +170,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
* @param lhs 此对象
|
* @param lhs 此对象
|
||||||
* @param rhs 另一个对象
|
* @param rhs 另一个对象
|
||||||
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
|
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
|
||||||
* @return 两个对象是否equals,是返回<code>true</code>
|
* @return 两个对象是否equals,是返回{@code true}
|
||||||
*/
|
*/
|
||||||
public static boolean reflectionEquals(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
|
public static boolean reflectionEquals(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
|
||||||
return reflectionEquals(lhs, rhs, ArrayUtil.toArray(excludeFields, String.class));
|
return reflectionEquals(lhs, rhs, ArrayUtil.toArray(excludeFields, String.class));
|
||||||
@ -182,62 +182,62 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
* @param lhs 此对象
|
* @param lhs 此对象
|
||||||
* @param rhs 另一个对象
|
* @param rhs 另一个对象
|
||||||
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
|
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
|
||||||
* @return 两个对象是否equals,是返回<code>true</code>
|
* @return 两个对象是否equals,是返回{@code true}
|
||||||
*/
|
*/
|
||||||
public static boolean reflectionEquals(final Object lhs, final Object rhs, final String... excludeFields) {
|
public static boolean reflectionEquals(final Object lhs, final Object rhs, final String... excludeFields) {
|
||||||
return reflectionEquals(lhs, rhs, false, null, excludeFields);
|
return reflectionEquals(lhs, rhs, false, null, excludeFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>This method uses reflection to determine if the two <code>Object</code>s
|
* <p>This method uses reflection to determine if the two {@code Object}s
|
||||||
* are equal.</p>
|
* are equal.</p>
|
||||||
*
|
*
|
||||||
* <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
|
* <p>It uses {@code AccessibleObject.setAccessible} to gain access to private
|
||||||
* fields. This means that it will throw a security exception if run under
|
* 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
|
* 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
|
* not as efficient as testing explicitly. Non-primitive fields are compared using
|
||||||
* <code>equals()</code>.</p>
|
* {@code equals()}.</p>
|
||||||
*
|
*
|
||||||
* <p>If the TestTransients parameter is set to <code>true</code>, transient
|
* <p>If the TestTransients parameter is set to {@code true}, transient
|
||||||
* members will be tested, otherwise they are ignored, as they are likely
|
* members will be tested, otherwise they are ignored, as they are likely
|
||||||
* derived fields, and not part of the value of the <code>Object</code>.</p>
|
* derived fields, and not part of the value of the {@code Object}.</p>
|
||||||
*
|
*
|
||||||
* <p>Static fields will not be tested. Superclass fields will be included.</p>
|
* <p>Static fields will not be tested. Superclass fields will be included.</p>
|
||||||
*
|
*
|
||||||
* @param lhs <code>this</code> object
|
* @param lhs {@code this} object
|
||||||
* @param rhs the other object
|
* @param rhs the other object
|
||||||
* @param testTransients whether to include transient fields
|
* @param testTransients whether to include transient fields
|
||||||
* @return <code>true</code> 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) {
|
public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients) {
|
||||||
return reflectionEquals(lhs, rhs, testTransients, null);
|
return reflectionEquals(lhs, rhs, testTransients, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>This method uses reflection to determine if the two <code>Object</code>s
|
* <p>This method uses reflection to determine if the two {@code Object}s
|
||||||
* are equal.</p>
|
* are equal.</p>
|
||||||
*
|
*
|
||||||
* <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
|
* <p>It uses {@code AccessibleObject.setAccessible} to gain access to private
|
||||||
* fields. This means that it will throw a security exception if run under
|
* 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
|
* 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
|
* not as efficient as testing explicitly. Non-primitive fields are compared using
|
||||||
* <code>equals()</code>.</p>
|
* {@code equals()}.</p>
|
||||||
*
|
*
|
||||||
* <p>If the testTransients parameter is set to <code>true</code>, transient
|
* <p>If the testTransients parameter is set to {@code true}, transient
|
||||||
* members will be tested, otherwise they are ignored, as they are likely
|
* members will be tested, otherwise they are ignored, as they are likely
|
||||||
* derived fields, and not part of the value of the <code>Object</code>.</p>
|
* derived fields, and not part of the value of the {@code Object}.</p>
|
||||||
*
|
*
|
||||||
* <p>Static fields will not be included. Superclass fields will be appended
|
* <p>Static fields will not be included. Superclass fields will be appended
|
||||||
* up to and including the specified superclass. A null superclass is treated
|
* up to and including the specified superclass. A null superclass is treated
|
||||||
* as java.lang.Object.</p>
|
* as java.lang.Object.</p>
|
||||||
*
|
*
|
||||||
* @param lhs <code>this</code> object
|
* @param lhs {@code this} object
|
||||||
* @param rhs the other object
|
* @param rhs the other object
|
||||||
* @param testTransients whether to include transient fields
|
* @param testTransients whether to include transient fields
|
||||||
* @param reflectUpToClass the superclass to reflect up to (inclusive),
|
* @param reflectUpToClass the superclass to reflect up to (inclusive),
|
||||||
* may be <code>null</code>
|
* may be {@code null}
|
||||||
* @param excludeFields array of field names to exclude from testing
|
* @param excludeFields array of field names to exclude from testing
|
||||||
* @return <code>true</code> if the two Objects have tested equals.
|
* @return {@code true} if the two Objects have tested equals.
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients, final Class<?> reflectUpToClass,
|
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<Boolean> {
|
|||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds the result of <code>super.equals()</code> to this builder.</p>
|
* <p>Adds the result of {@code super.equals()} to this builder.</p>
|
||||||
*
|
*
|
||||||
* @param superEquals the result of calling <code>super.equals()</code>
|
* @param superEquals the result of calling {@code super.equals()}
|
||||||
* @return EqualsBuilder - used to chain calls.
|
* @return EqualsBuilder - used to chain calls.
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
@ -360,8 +360,8 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Test if two <code>Object</code>s are equal using their
|
* <p>Test if two {@code Object}s are equal using their
|
||||||
* <code>equals</code> method.</p>
|
* {@code equals} method.</p>
|
||||||
*
|
*
|
||||||
* @param lhs the left hand object
|
* @param lhs the left hand object
|
||||||
* @param rhs the right hand object
|
* @param rhs the right hand object
|
||||||
@ -388,11 +388,11 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Test if two <code>long</code> s are equal.
|
* Test if two {@code long} s are equal.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param lhs the left hand <code>long</code>
|
* @param lhs the left hand {@code long}
|
||||||
* @param rhs the right hand <code>long</code>
|
* @param rhs the right hand {@code long}
|
||||||
* @return EqualsBuilder - used to chain calls.
|
* @return EqualsBuilder - used to chain calls.
|
||||||
*/
|
*/
|
||||||
public EqualsBuilder append(final long lhs, final long rhs) {
|
public EqualsBuilder append(final long lhs, final long rhs) {
|
||||||
@ -404,10 +404,10 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Test if two <code>int</code>s are equal.</p>
|
* <p>Test if two {@code int}s are equal.</p>
|
||||||
*
|
*
|
||||||
* @param lhs the left hand <code>int</code>
|
* @param lhs the left hand {@code int}
|
||||||
* @param rhs the right hand <code>int</code>
|
* @param rhs the right hand {@code int}
|
||||||
* @return EqualsBuilder - used to chain calls.
|
* @return EqualsBuilder - used to chain calls.
|
||||||
*/
|
*/
|
||||||
public EqualsBuilder append(final int lhs, final int rhs) {
|
public EqualsBuilder append(final int lhs, final int rhs) {
|
||||||
@ -419,10 +419,10 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Test if two <code>short</code>s are equal.</p>
|
* <p>Test if two {@code short}s are equal.</p>
|
||||||
*
|
*
|
||||||
* @param lhs the left hand <code>short</code>
|
* @param lhs the left hand {@code short}
|
||||||
* @param rhs the right hand <code>short</code>
|
* @param rhs the right hand {@code short}
|
||||||
* @return EqualsBuilder - used to chain calls.
|
* @return EqualsBuilder - used to chain calls.
|
||||||
*/
|
*/
|
||||||
public EqualsBuilder append(final short lhs, final short rhs) {
|
public EqualsBuilder append(final short lhs, final short rhs) {
|
||||||
@ -434,10 +434,10 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Test if two <code>char</code>s are equal.</p>
|
* <p>Test if two {@code char}s are equal.</p>
|
||||||
*
|
*
|
||||||
* @param lhs the left hand <code>char</code>
|
* @param lhs the left hand {@code char}
|
||||||
* @param rhs the right hand <code>char</code>
|
* @param rhs the right hand {@code char}
|
||||||
* @return EqualsBuilder - used to chain calls.
|
* @return EqualsBuilder - used to chain calls.
|
||||||
*/
|
*/
|
||||||
public EqualsBuilder append(final char lhs, final char rhs) {
|
public EqualsBuilder append(final char lhs, final char rhs) {
|
||||||
@ -449,10 +449,10 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Test if two <code>byte</code>s are equal.</p>
|
* <p>Test if two {@code byte}s are equal.</p>
|
||||||
*
|
*
|
||||||
* @param lhs the left hand <code>byte</code>
|
* @param lhs the left hand {@code byte}
|
||||||
* @param rhs the right hand <code>byte</code>
|
* @param rhs the right hand {@code byte}
|
||||||
* @return EqualsBuilder - used to chain calls.
|
* @return EqualsBuilder - used to chain calls.
|
||||||
*/
|
*/
|
||||||
public EqualsBuilder append(final byte lhs, final byte rhs) {
|
public EqualsBuilder append(final byte lhs, final byte rhs) {
|
||||||
@ -464,16 +464,16 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Test if two <code>double</code>s are equal by testing that the
|
* <p>Test if two {@code double}s are equal by testing that the
|
||||||
* pattern of bits returned by <code>doubleToLong</code> are equal.</p>
|
* pattern of bits returned by {@code doubleToLong} are equal.</p>
|
||||||
*
|
*
|
||||||
* <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
|
* <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
|
||||||
*
|
*
|
||||||
* <p>It is compatible with the hash code generated by
|
* <p>It is compatible with the hash code generated by
|
||||||
* <code>HashCodeBuilder</code>.</p>
|
* {@code HashCodeBuilder}.</p>
|
||||||
*
|
*
|
||||||
* @param lhs the left hand <code>double</code>
|
* @param lhs the left hand {@code double}
|
||||||
* @param rhs the right hand <code>double</code>
|
* @param rhs the right hand {@code double}
|
||||||
* @return EqualsBuilder - used to chain calls.
|
* @return EqualsBuilder - used to chain calls.
|
||||||
*/
|
*/
|
||||||
public EqualsBuilder append(final double lhs, final double rhs) {
|
public EqualsBuilder append(final double lhs, final double rhs) {
|
||||||
@ -484,16 +484,16 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Test if two <code>float</code>s are equal byt testing that the
|
* <p>Test if two {@code float}s are equal byt testing that the
|
||||||
* pattern of bits returned by doubleToLong are equal.</p>
|
* pattern of bits returned by doubleToLong are equal.</p>
|
||||||
*
|
*
|
||||||
* <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
|
* <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
|
||||||
*
|
*
|
||||||
* <p>It is compatible with the hash code generated by
|
* <p>It is compatible with the hash code generated by
|
||||||
* <code>HashCodeBuilder</code>.</p>
|
* {@code HashCodeBuilder}.</p>
|
||||||
*
|
*
|
||||||
* @param lhs the left hand <code>float</code>
|
* @param lhs the left hand {@code float}
|
||||||
* @param rhs the right hand <code>float</code>
|
* @param rhs the right hand {@code float}
|
||||||
* @return EqualsBuilder - used to chain calls.
|
* @return EqualsBuilder - used to chain calls.
|
||||||
*/
|
*/
|
||||||
public EqualsBuilder append(final float lhs, final float rhs) {
|
public EqualsBuilder append(final float lhs, final float rhs) {
|
||||||
@ -504,10 +504,10 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Test if two <code>booleans</code>s are equal.</p>
|
* <p>Test if two {@code booleans}s are equal.</p>
|
||||||
*
|
*
|
||||||
* @param lhs the left hand <code>boolean</code>
|
* @param lhs the left hand {@code boolean}
|
||||||
* @param rhs the right hand <code>boolean</code>
|
* @param rhs the right hand {@code boolean}
|
||||||
* @return EqualsBuilder - used to chain calls.
|
* @return EqualsBuilder - used to chain calls.
|
||||||
*/
|
*/
|
||||||
public EqualsBuilder append(final boolean lhs, final boolean rhs) {
|
public EqualsBuilder append(final boolean lhs, final boolean rhs) {
|
||||||
@ -519,7 +519,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns <code>true</code> if the fields that have been checked
|
* <p>Returns {@code true} if the fields that have been checked
|
||||||
* are all equal.</p>
|
* are all equal.</p>
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
@ -529,11 +529,11 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns <code>true</code> if the fields that have been checked
|
* <p>Returns {@code true} if the fields that have been checked
|
||||||
* are all equal.</p>
|
* are all equal.</p>
|
||||||
*
|
*
|
||||||
* @return <code>true</code> if all of the fields that have been checked
|
* @return {@code true} if all of the fields that have been checked
|
||||||
* are equal, <code>false</code> otherwise.
|
* are equal, {@code false} otherwise.
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ -542,7 +542,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the <code>isEquals</code> value.
|
* Sets the {@code isEquals} value.
|
||||||
*
|
*
|
||||||
* @param isEquals The value to set.
|
* @param isEquals The value to set.
|
||||||
* @return this
|
* @return this
|
||||||
|
@ -906,4 +906,19 @@ public class IterUtil {
|
|||||||
// 当两个Iterable长度不一致时返回false
|
// 当两个Iterable长度不一致时返回false
|
||||||
return false == (it1.hasNext() || it2.hasNext());
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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<br>
|
||||||
|
* 通过自定义唯一键,通过{@link #uniqueGenerator}生成节点对象对应的键作为Map的key,确定唯一<br>
|
||||||
|
* 此Set与HashSet不同的是,HashSet依赖于{@link Object#equals(Object)}确定唯一<br>
|
||||||
|
* 但是很多时候我们无法对对象进行修改,此时在外部定义一个唯一规则,即可完成去重。
|
||||||
|
* <pre>
|
||||||
|
* {@code Set<UniqueTestBean> set = new UniqueKeySet<>(UniqueTestBean::getId);}
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param <K> 唯一键类型
|
||||||
|
* @param <V> 值对象
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public class UniqueKeySet<K, V> extends AbstractSet<V> implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Map<K, V> map;
|
||||||
|
private final Function<V, K> uniqueGenerator;
|
||||||
|
|
||||||
|
//region 构造
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
|
||||||
|
*/
|
||||||
|
public UniqueKeySet(Function<V, K> uniqueGenerator) {
|
||||||
|
this(false, uniqueGenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param isLinked 是否保持加入顺序
|
||||||
|
* @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
|
||||||
|
*/
|
||||||
|
public UniqueKeySet(boolean isLinked, Function<V, K> uniqueGenerator) {
|
||||||
|
this(MapBuilder.create(isLinked), uniqueGenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param initialCapacity 初始容量
|
||||||
|
* @param loadFactor 增长因子
|
||||||
|
* @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
|
||||||
|
*/
|
||||||
|
public UniqueKeySet(int initialCapacity, float loadFactor, Function<V, K> uniqueGenerator) {
|
||||||
|
this(MapBuilder.create(new HashMap<>(initialCapacity, loadFactor)), uniqueGenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param builder 初始Map,定义了Map类型
|
||||||
|
* @param uniqueGenerator 唯一键生成规则函数,用于生成对象对应的唯一键
|
||||||
|
*/
|
||||||
|
public UniqueKeySet(MapBuilder<K, V> builder, Function<V, K> uniqueGenerator) {
|
||||||
|
this.map = builder.build();
|
||||||
|
this.uniqueGenerator = uniqueGenerator;
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<V> 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<K, V> clone() {
|
||||||
|
try {
|
||||||
|
UniqueKeySet<K, V> newSet = (UniqueKeySet<K, V>) super.clone();
|
||||||
|
newSet.map = ObjectUtil.clone(this.map);
|
||||||
|
return newSet;
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
throw new InternalError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -77,6 +77,8 @@ import java.util.concurrent.atomic.AtomicIntegerArray;
|
|||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.concurrent.atomic.AtomicLongArray;
|
import java.util.concurrent.atomic.AtomicLongArray;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
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(Integer.class, new NumberConverter(Integer.class));
|
||||||
defaultConverterMap.put(AtomicInteger.class, new NumberConverter(AtomicInteger.class));// since 3.0.8
|
defaultConverterMap.put(AtomicInteger.class, new NumberConverter(AtomicInteger.class));// since 3.0.8
|
||||||
defaultConverterMap.put(Long.class, new NumberConverter(Long.class));
|
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(AtomicLong.class, new NumberConverter(AtomicLong.class));// since 3.0.8
|
||||||
defaultConverterMap.put(Byte.class, new NumberConverter(Byte.class));
|
defaultConverterMap.put(Byte.class, new NumberConverter(Byte.class));
|
||||||
defaultConverterMap.put(Short.class, new NumberConverter(Short.class));
|
defaultConverterMap.put(Short.class, new NumberConverter(Short.class));
|
||||||
defaultConverterMap.put(Float.class, new NumberConverter(Float.class));
|
defaultConverterMap.put(Float.class, new NumberConverter(Float.class));
|
||||||
defaultConverterMap.put(Double.class, new NumberConverter(Double.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(Character.class, new CharacterConverter());
|
||||||
defaultConverterMap.put(Boolean.class, new BooleanConverter());
|
defaultConverterMap.put(Boolean.class, new BooleanConverter());
|
||||||
defaultConverterMap.put(AtomicBoolean.class, new AtomicBooleanConverter());// since 3.0.8
|
defaultConverterMap.put(AtomicBoolean.class, new AtomicBooleanConverter());// since 3.0.8
|
||||||
|
@ -188,7 +188,7 @@ public class NumberChineseFormatter {
|
|||||||
Assert.checkBetween(amount, -999, 999, "Number support only: (-999 ~ 999)!");
|
Assert.checkBetween(amount, -999, 999, "Number support only: (-999 ~ 999)!");
|
||||||
|
|
||||||
final String chinese = thousandToChinese(amount, isUseTraditional);
|
final String chinese = thousandToChinese(amount, isUseTraditional);
|
||||||
if(amount < 20 && amount > 10){
|
if(amount < 20 && amount >= 10){
|
||||||
// "十一"而非"一十一"
|
// "十一"而非"一十一"
|
||||||
return chinese.substring(1);
|
return chinese.substring(1);
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ public class NumberConverter extends AbstractConverter<Number> {
|
|||||||
return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseFloat(valueStr);
|
return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseFloat(valueStr);
|
||||||
} else if (Double.class == targetType) {
|
} else if (Double.class == targetType) {
|
||||||
if (value instanceof Number) {
|
if (value instanceof Number) {
|
||||||
return ((Number) value).doubleValue();
|
return NumberUtil.toDouble((Number) value);
|
||||||
} else if (value instanceof Boolean) {
|
} else if (value instanceof Boolean) {
|
||||||
return BooleanUtil.toDoubleObj((Boolean) value);
|
return BooleanUtil.toDoubleObj((Boolean) value);
|
||||||
}
|
}
|
||||||
@ -194,7 +194,7 @@ public class NumberConverter extends AbstractConverter<Number> {
|
|||||||
return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseDouble(valueStr);
|
return StrUtil.isBlank(valueStr) ? null : NumberUtil.parseDouble(valueStr);
|
||||||
} else if (DoubleAdder.class == targetType) {
|
} else if (DoubleAdder.class == targetType) {
|
||||||
//jdk8 新增
|
//jdk8 新增
|
||||||
final Number number = convert(value, Long.class, toStrFunc);
|
final Number number = convert(value, Double.class, toStrFunc);
|
||||||
if (null != number) {
|
if (null != number) {
|
||||||
final DoubleAdder doubleAdder = new DoubleAdder();
|
final DoubleAdder doubleAdder = new DoubleAdder();
|
||||||
doubleAdder.add(number.doubleValue());
|
doubleAdder.add(number.doubleValue());
|
||||||
|
@ -22,6 +22,21 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
* 4. INC 自增计数器。确保同一秒内产生objectId的唯一性。
|
* 4. INC 自增计数器。确保同一秒内产生objectId的唯一性。
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
* <table summary="" border="1">
|
||||||
|
* <tr>
|
||||||
|
* <td>时间戳</td>
|
||||||
|
* <td>机器ID</td>
|
||||||
|
* <td>进程ID</td>
|
||||||
|
* <td>自增计数器</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>4</td>
|
||||||
|
* <td>3</td>
|
||||||
|
* <td>2</td>
|
||||||
|
* <td>3</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
* 参考:http://blog.csdn.net/qxc1281/article/details/54021882
|
* 参考:http://blog.csdn.net/qxc1281/article/details/54021882
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
|
46
hutool-core/src/main/java/cn/hutool/core/map/AbsEntry.java
Normal file
46
hutool-core/src/main/java/cn/hutool/core/map/AbsEntry.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package cn.hutool.core.map;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象的{@link Map.Entry}实现,来自Guava<br>
|
||||||
|
* 实现了默认的{@link #equals(Object)}、{@link #hashCode()}、{@link #toString()}方法。<br>
|
||||||
|
* 默认{@link #setValue(Object)}抛出异常。
|
||||||
|
*
|
||||||
|
* @param <K> 键类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author Guava
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public abstract class AbsEntry<K, V> implements Map.Entry<K, V> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V setValue(V value) {
|
||||||
|
throw new UnsupportedOperationException("Entry is read only.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof Map.Entry) {
|
||||||
|
final Map.Entry<?, ?> that = (Map.Entry<?, ?>) object;
|
||||||
|
return ObjectUtil.equals(this.getKey(), that.getKey())
|
||||||
|
&& ObjectUtil.equals(this.getValue(), that.getValue());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
//copy from 1.8 HashMap.Node
|
||||||
|
K k = getKey();
|
||||||
|
V v = getValue();
|
||||||
|
return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getKey() + "=" + getValue();
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,5 @@
|
|||||||
package cn.hutool.core.map;
|
package cn.hutool.core.map;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -15,7 +13,7 @@ import java.util.Map;
|
|||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
* @since 4.0.7
|
* @since 4.0.7
|
||||||
*/
|
*/
|
||||||
public class CamelCaseLinkedMap<K, V> extends CustomKeyMap<K, V> {
|
public class CamelCaseLinkedMap<K, V> extends CamelCaseMap<K, V> {
|
||||||
private static final long serialVersionUID = 4043263744224569870L;
|
private static final long serialVersionUID = 4043263744224569870L;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------- Constructor start
|
// ------------------------------------------------------------------------- Constructor start
|
||||||
@ -48,7 +46,7 @@ public class CamelCaseLinkedMap<K, V> extends CustomKeyMap<K, V> {
|
|||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
* @param loadFactor 加载因子
|
* @param loadFactor 加载因子
|
||||||
* @param m Map
|
* @param m Map,数据会被默认拷贝到一个新的LinkedHashMap中
|
||||||
*/
|
*/
|
||||||
public CamelCaseLinkedMap(float loadFactor, Map<? extends K, ? extends V> m) {
|
public CamelCaseLinkedMap(float loadFactor, Map<? extends K, ? extends V> m) {
|
||||||
this(m.size(), loadFactor);
|
this(m.size(), loadFactor);
|
||||||
@ -65,18 +63,4 @@ public class CamelCaseLinkedMap<K, V> extends CustomKeyMap<K, V> {
|
|||||||
super(new LinkedHashMap<>(initialCapacity, loadFactor));
|
super(new LinkedHashMap<>(initialCapacity, loadFactor));
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------- Constructor end
|
// ------------------------------------------------------------------------- Constructor end
|
||||||
|
|
||||||
/**
|
|
||||||
* 将Key转为驼峰风格,如果key为字符串的话
|
|
||||||
*
|
|
||||||
* @param key KEY
|
|
||||||
* @return 驼峰Key
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Object customKey(Object key) {
|
|
||||||
if (key instanceof CharSequence) {
|
|
||||||
key = StrUtil.toCamelCase(key.toString());
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
package cn.hutool.core.map;
|
package cn.hutool.core.map;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 驼峰Key风格的Map<br>
|
* 驼峰Key风格的Map<br>
|
||||||
* 对KEY转换为驼峰,get("int_value")和get("intValue")获得的值相同,put进入的值也会被覆盖
|
* 对KEY转换为驼峰,get("int_value")和get("intValue")获得的值相同,put进入的值也会被覆盖
|
||||||
*
|
*
|
||||||
* @author Looly
|
|
||||||
*
|
|
||||||
* @param <K> 键类型
|
* @param <K> 键类型
|
||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
|
* @author Looly
|
||||||
* @since 4.0.7
|
* @since 4.0.7
|
||||||
*/
|
*/
|
||||||
public class CamelCaseMap<K, V> extends CustomKeyMap<K, V> {
|
public class CamelCaseMap<K, V> extends FuncKeyMap<K, V> {
|
||||||
private static final long serialVersionUID = 4043263744224569870L;
|
private static final long serialVersionUID = 4043263744224569870L;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------- Constructor start
|
// ------------------------------------------------------------------------- Constructor start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*/
|
*/
|
||||||
@ -48,7 +48,7 @@ public class CamelCaseMap<K, V> extends CustomKeyMap<K, V> {
|
|||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
* @param loadFactor 加载因子
|
* @param loadFactor 加载因子
|
||||||
* @param m Map
|
* @param m 初始Map,数据会被默认拷贝到一个新的HashMap中
|
||||||
*/
|
*/
|
||||||
public CamelCaseMap(float loadFactor, Map<? extends K, ? extends V> m) {
|
public CamelCaseMap(float loadFactor, Map<? extends K, ? extends V> m) {
|
||||||
this(m.size(), loadFactor);
|
this(m.size(), loadFactor);
|
||||||
@ -59,24 +59,26 @@ public class CamelCaseMap<K, V> extends CustomKeyMap<K, V> {
|
|||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
* @param initialCapacity 初始大小
|
* @param initialCapacity 初始大小
|
||||||
* @param loadFactor 加载因子
|
* @param loadFactor 加载因子
|
||||||
*/
|
*/
|
||||||
public CamelCaseMap(int initialCapacity, float loadFactor) {
|
public CamelCaseMap(int initialCapacity, float loadFactor) {
|
||||||
super(new HashMap<>(initialCapacity, loadFactor));
|
this(MapBuilder.create(new HashMap<>(initialCapacity, loadFactor)));
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------- Constructor end
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将Key转为驼峰风格,如果key为字符串的话
|
* 构造<br>
|
||||||
|
* 注意此构造将传入的Map作为被包装的Map,针对任何修改,传入的Map都会被同样修改。
|
||||||
*
|
*
|
||||||
* @param key KEY
|
* @param emptyMapBuilder Map构造器,必须构造空的Map
|
||||||
* @return 驼峰Key
|
|
||||||
*/
|
*/
|
||||||
@Override
|
CamelCaseMap(MapBuilder<K, V> emptyMapBuilder) {
|
||||||
protected Object customKey(Object key) {
|
super(emptyMapBuilder.build(), (key) -> {
|
||||||
if (key instanceof CharSequence) {
|
if (key instanceof CharSequence) {
|
||||||
key = StrUtil.toCamelCase(key.toString());
|
key = StrUtil.toCamelCase(key.toString());
|
||||||
}
|
}
|
||||||
return key;
|
//noinspection unchecked
|
||||||
|
return (K) key;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
// ------------------------------------------------------------------------- Constructor end
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import java.util.Map;
|
|||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
* @since 3.3.1
|
* @since 3.3.1
|
||||||
*/
|
*/
|
||||||
public class CaseInsensitiveLinkedMap<K, V> extends CustomKeyMap<K, V> {
|
public class CaseInsensitiveLinkedMap<K, V> extends CaseInsensitiveMap<K, V> {
|
||||||
private static final long serialVersionUID = 4043263744224569870L;
|
private static final long serialVersionUID = 4043263744224569870L;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------- Constructor start
|
// ------------------------------------------------------------------------- Constructor start
|
||||||
@ -64,18 +64,4 @@ public class CaseInsensitiveLinkedMap<K, V> extends CustomKeyMap<K, V> {
|
|||||||
super(new LinkedHashMap<>(initialCapacity, loadFactor));
|
super(new LinkedHashMap<>(initialCapacity, loadFactor));
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------- Constructor end
|
// ------------------------------------------------------------------------- Constructor end
|
||||||
|
|
||||||
/**
|
|
||||||
* 将Key转为小写
|
|
||||||
*
|
|
||||||
* @param key KEY
|
|
||||||
* @return 小写KEY
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Object customKey(Object key) {
|
|
||||||
if (key instanceof CharSequence) {
|
|
||||||
key = key.toString().toLowerCase();
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import java.util.Map;
|
|||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
* @since 3.0.2
|
* @since 3.0.2
|
||||||
*/
|
*/
|
||||||
public class CaseInsensitiveMap<K, V> extends CustomKeyMap<K, V> {
|
public class CaseInsensitiveMap<K, V> extends FuncKeyMap<K, V> {
|
||||||
private static final long serialVersionUID = 4043263744224569870L;
|
private static final long serialVersionUID = 4043263744224569870L;
|
||||||
|
|
||||||
//------------------------------------------------------------------------- Constructor start
|
//------------------------------------------------------------------------- Constructor start
|
||||||
@ -34,9 +34,10 @@ public class CaseInsensitiveMap<K, V> extends CustomKeyMap<K, V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造<br>
|
||||||
|
* 注意此构造将传入的Map作为被包装的Map,针对任何修改,传入的Map都会被同样修改。
|
||||||
*
|
*
|
||||||
* @param m Map
|
* @param m 被包装的自定义Map创建器
|
||||||
*/
|
*/
|
||||||
public CaseInsensitiveMap(Map<? extends K, ? extends V> m) {
|
public CaseInsensitiveMap(Map<? extends K, ? extends V> m) {
|
||||||
this(DEFAULT_LOAD_FACTOR, m);
|
this(DEFAULT_LOAD_FACTOR, m);
|
||||||
@ -61,21 +62,23 @@ public class CaseInsensitiveMap<K, V> extends CustomKeyMap<K, V> {
|
|||||||
* @param loadFactor 加载因子
|
* @param loadFactor 加载因子
|
||||||
*/
|
*/
|
||||||
public CaseInsensitiveMap(int initialCapacity, float loadFactor) {
|
public CaseInsensitiveMap(int initialCapacity, float loadFactor) {
|
||||||
super(new HashMap<>(initialCapacity, loadFactor));
|
this(MapBuilder.create(new HashMap<>(initialCapacity, loadFactor)));
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------- Constructor end
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将Key转为小写
|
* 构造<br>
|
||||||
|
* 注意此构造将传入的Map作为被包装的Map,针对任何修改,传入的Map都会被同样修改。
|
||||||
*
|
*
|
||||||
* @param key KEY
|
* @param emptyMapBuilder 被包装的自定义Map创建器
|
||||||
* @return 小写KEY
|
|
||||||
*/
|
*/
|
||||||
@Override
|
CaseInsensitiveMap(MapBuilder<K, V> emptyMapBuilder) {
|
||||||
protected Object customKey(Object key) {
|
super(emptyMapBuilder.build(), (key)->{
|
||||||
if (key instanceof CharSequence) {
|
if (key instanceof CharSequence) {
|
||||||
key = key.toString().toLowerCase();
|
key = key.toString().toLowerCase();
|
||||||
}
|
}
|
||||||
return key;
|
//noinspection unchecked
|
||||||
|
return (K) key;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
//------------------------------------------------------------------------- Constructor end
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import java.util.TreeMap;
|
|||||||
* @param <V> 值类型
|
* @param <V> 值类型
|
||||||
* @since 3.3.1
|
* @since 3.3.1
|
||||||
*/
|
*/
|
||||||
public class CaseInsensitiveTreeMap<K, V> extends CustomKeyMap<K, V> {
|
public class CaseInsensitiveTreeMap<K, V> extends CaseInsensitiveMap<K, V> {
|
||||||
private static final long serialVersionUID = 4043263744224569870L;
|
private static final long serialVersionUID = 4043263744224569870L;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------- Constructor start
|
// ------------------------------------------------------------------------- Constructor start
|
||||||
@ -40,7 +40,7 @@ public class CaseInsensitiveTreeMap<K, V> extends CustomKeyMap<K, V> {
|
|||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
* @param m Map
|
* @param m Map,初始Map,键值对会被复制到新的TreeMap中
|
||||||
* @since 3.1.2
|
* @since 3.1.2
|
||||||
*/
|
*/
|
||||||
public CaseInsensitiveTreeMap(SortedMap<? extends K, ? extends V> m) {
|
public CaseInsensitiveTreeMap(SortedMap<? extends K, ? extends V> m) {
|
||||||
@ -56,18 +56,4 @@ public class CaseInsensitiveTreeMap<K, V> extends CustomKeyMap<K, V> {
|
|||||||
super(new TreeMap<>(comparator));
|
super(new TreeMap<>(comparator));
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------- Constructor end
|
// ------------------------------------------------------------------------- Constructor end
|
||||||
|
|
||||||
/**
|
|
||||||
* 将Key转为小写
|
|
||||||
*
|
|
||||||
* @param key KEY
|
|
||||||
* @return 小写KEY
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Object customKey(Object key) {
|
|
||||||
if (key instanceof CharSequence) {
|
|
||||||
key = key.toString().toLowerCase();
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,11 @@ public abstract class CustomKeyMap<K, V> extends MapWrapper<K, V> {
|
|||||||
* 构造<br>
|
* 构造<br>
|
||||||
* 通过传入一个Map从而确定Map的类型,子类需创建一个空的Map,而非传入一个已有Map,否则值可能会被修改
|
* 通过传入一个Map从而确定Map的类型,子类需创建一个空的Map,而非传入一个已有Map,否则值可能会被修改
|
||||||
*
|
*
|
||||||
* @param m Map 被包装的Map
|
* @param emptyMap Map 被包装的Map,必须为空Map,否则自定义key会无效
|
||||||
* @since 3.1.2
|
* @since 3.1.2
|
||||||
*/
|
*/
|
||||||
public CustomKeyMap(Map<K, V> m) {
|
public CustomKeyMap(Map<K, V> emptyMap) {
|
||||||
super(m);
|
super(emptyMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3,7 +3,8 @@ package cn.hutool.core.map;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 固定大小的{@link LinkedHashMap} 实现
|
* 固定大小的{@link LinkedHashMap} 实现<br>
|
||||||
|
* 注意此类非线程安全,由于{@link #get(Object)}操作会修改链表的顺序结构,因此也不可以使用读写锁。
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
*
|
*
|
||||||
|
@ -19,13 +19,14 @@ public class FuncKeyMap<K, V> extends CustomKeyMap<K, V> {
|
|||||||
// ------------------------------------------------------------------------- Constructor start
|
// ------------------------------------------------------------------------- Constructor start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造<br>
|
||||||
|
* 注意提供的Map中不能有键值对,否则可能导致自定义key失效
|
||||||
*
|
*
|
||||||
* @param m Map
|
* @param emptyMap Map,提供的空map
|
||||||
* @param keyFunc 自定义KEY的函数
|
* @param keyFunc 自定义KEY的函数
|
||||||
*/
|
*/
|
||||||
public FuncKeyMap(Map<K, V> m, Function<Object, K> keyFunc) {
|
public FuncKeyMap(Map<K, V> emptyMap, Function<Object, K> keyFunc) {
|
||||||
super(m);
|
super(emptyMap);
|
||||||
this.keyFunc = keyFunc;
|
this.keyFunc = keyFunc;
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------- Constructor end
|
// ------------------------------------------------------------------------- Constructor end
|
||||||
|
@ -13,7 +13,7 @@ import java.util.function.Supplier;
|
|||||||
* @param <V> Value类型
|
* @param <V> Value类型
|
||||||
* @since 3.1.1
|
* @since 3.1.1
|
||||||
*/
|
*/
|
||||||
public class MapBuilder<K, V> implements Builder<Map<K,V>> {
|
public class MapBuilder<K, V> implements Builder<Map<K, V>> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final Map<K, V> map;
|
private final Map<K, V> map;
|
||||||
@ -120,6 +120,17 @@ public class MapBuilder<K, V> implements Builder<Map<K,V>> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空Map
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public MapBuilder<K, V> clear() {
|
||||||
|
this.map.clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建后的map
|
* 创建后的map
|
||||||
*
|
*
|
||||||
|
@ -88,7 +88,6 @@ public class MapWrapper<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, S
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("NullableProblems")
|
|
||||||
public void putAll(Map<? extends K, ? extends V> m) {
|
public void putAll(Map<? extends K, ? extends V> m) {
|
||||||
raw.putAll(m);
|
raw.putAll(m);
|
||||||
}
|
}
|
||||||
@ -99,25 +98,21 @@ public class MapWrapper<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, S
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("NullableProblems")
|
|
||||||
public Collection<V> values() {
|
public Collection<V> values() {
|
||||||
return raw.values();
|
return raw.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("NullableProblems")
|
|
||||||
public Set<K> keySet() {
|
public Set<K> keySet() {
|
||||||
return raw.keySet();
|
return raw.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("NullableProblems")
|
|
||||||
public Set<Entry<K, V>> entrySet() {
|
public Set<Entry<K, V>> entrySet() {
|
||||||
return raw.entrySet();
|
return raw.entrySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("NullableProblems")
|
|
||||||
public Iterator<Entry<K, V>> iterator() {
|
public Iterator<Entry<K, V>> iterator() {
|
||||||
return this.entrySet().iterator();
|
return this.entrySet().iterator();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package cn.hutool.core.map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link java.util.Map.Entry}简单实现。<br>
|
||||||
|
* 键值对使用不可变字段表示。
|
||||||
|
*
|
||||||
|
* @param <K> 键类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public class SimpleEntry<K, V> extends AbsEntry<K, V> {
|
||||||
|
|
||||||
|
private final K key;
|
||||||
|
private final V value;
|
||||||
|
|
||||||
|
public SimpleEntry(K key, V value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,6 @@ import java.util.Iterator;
|
|||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,7 +172,7 @@ public class TableMap<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Ser
|
|||||||
public Set<Map.Entry<K, V>> entrySet() {
|
public Set<Map.Entry<K, V>> entrySet() {
|
||||||
final Set<Map.Entry<K, V>> hashSet = new LinkedHashSet<>();
|
final Set<Map.Entry<K, V>> hashSet = new LinkedHashSet<>();
|
||||||
for (int i = 0; i < size(); i++) {
|
for (int i = 0; i < size(); i++) {
|
||||||
hashSet.add(new Entry<>(keys.get(i), values.get(i)));
|
hashSet.add(new SimpleEntry<>(keys.get(i), values.get(i)));
|
||||||
}
|
}
|
||||||
return hashSet;
|
return hashSet;
|
||||||
}
|
}
|
||||||
@ -191,7 +190,7 @@ public class TableMap<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Ser
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map.Entry<K, V> next() {
|
public Map.Entry<K, V> next() {
|
||||||
return new Entry<>(keysIter.next(), valuesIter.next());
|
return new SimpleEntry<>(keysIter.next(), valuesIter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -209,48 +208,4 @@ public class TableMap<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Ser
|
|||||||
", values=" + values +
|
", values=" + values +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Entry<K, V> implements Map.Entry<K, V> {
|
|
||||||
|
|
||||||
private final K key;
|
|
||||||
private final V value;
|
|
||||||
|
|
||||||
public Entry(K key, V value) {
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public K getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V setValue(V value) {
|
|
||||||
throw new UnsupportedOperationException("setValue not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean equals(Object o) {
|
|
||||||
if (o == this)
|
|
||||||
return true;
|
|
||||||
if (o instanceof Map.Entry) {
|
|
||||||
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
|
|
||||||
return Objects.equals(key, e.getKey()) &&
|
|
||||||
Objects.equals(value, e.getValue());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
//copy from 1.8 HashMap.Node
|
|
||||||
return Objects.hashCode(key) ^ Objects.hashCode(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
236
hutool-core/src/main/java/cn/hutool/core/map/multi/AbsTable.java
Normal file
236
hutool-core/src/main/java/cn/hutool/core/map/multi/AbsTable.java
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
package cn.hutool.core.map.multi;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.IterUtil;
|
||||||
|
import cn.hutool.core.collection.TransIter;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.AbstractCollection;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象{@link Table}接口实现<br>
|
||||||
|
* 默认实现了:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #equals(Object)}</li>
|
||||||
|
* <li>{@link #hashCode()}</li>
|
||||||
|
* <li>{@link #toString()}</li>
|
||||||
|
* <li>{@link #values()}</li>
|
||||||
|
* <li>{@link #cellSet()}</li>
|
||||||
|
* <li>{@link #iterator()}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param <R> 行类型
|
||||||
|
* @param <C> 列类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author Guava, Looly
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public abstract class AbsTable<R, C, V> implements Table<R, C, V> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
} else if (obj instanceof Table) {
|
||||||
|
final Table<?, ?, ?> that = (Table<?, ?, ?>) obj;
|
||||||
|
return this.cellSet().equals(that.cellSet());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return cellSet().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return rowMap().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
//region values
|
||||||
|
@Override
|
||||||
|
public Collection<V> values() {
|
||||||
|
Collection<V> result = values;
|
||||||
|
return (result == null) ? values = new Values() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<V> values;
|
||||||
|
private class Values extends AbstractCollection<V> {
|
||||||
|
@Override
|
||||||
|
public Iterator<V> iterator() {
|
||||||
|
return new TransIter<>(cellSet().iterator(), Cell::getValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return containsValue((V) o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
AbsTable.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return AbsTable.this.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region cellSet
|
||||||
|
@Override
|
||||||
|
public Set<Cell<R, C, V>> cellSet() {
|
||||||
|
Set<Cell<R, C, V>> result = cellSet;
|
||||||
|
return (result == null) ? cellSet = new CellSet() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Cell<R, C, V>> cellSet;
|
||||||
|
|
||||||
|
private class CellSet extends AbstractSet<Cell<R, C, V>> {
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
if (o instanceof Cell) {
|
||||||
|
@SuppressWarnings("unchecked") final Cell<R, C, V> cell = (Cell<R, C, V>) o;
|
||||||
|
Map<C, V> row = getRow(cell.getRowKey());
|
||||||
|
if (null != row) {
|
||||||
|
return ObjectUtil.equals(row.get(cell.getColumnKey()), cell.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
if (contains(o)) {
|
||||||
|
@SuppressWarnings("unchecked") final Cell<R, C, V> cell = (Cell<R, C, V>) o;
|
||||||
|
AbsTable.this.remove(cell.getRowKey(), cell.getColumnKey());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
AbsTable.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Table.Cell<R, C, V>> iterator() {
|
||||||
|
return new AbsTable<R, C, V>.CellIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return AbsTable.this.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region iterator
|
||||||
|
@Override
|
||||||
|
public Iterator<Cell<R, C, V>> iterator() {
|
||||||
|
return new CellIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于{@link Cell}的{@link Iterator}实现
|
||||||
|
*/
|
||||||
|
private class CellIterator implements Iterator<Cell<R, C, V>> {
|
||||||
|
final Iterator<Map.Entry<R, Map<C, V>>> rowIterator = rowMap().entrySet().iterator();
|
||||||
|
Map.Entry<R, Map<C, V>> rowEntry;
|
||||||
|
Iterator<Map.Entry<C, V>> columnIterator = IterUtil.empty();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return rowIterator.hasNext() || columnIterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cell<R, C, V> next() {
|
||||||
|
if (false == columnIterator.hasNext()) {
|
||||||
|
rowEntry = rowIterator.next();
|
||||||
|
columnIterator = rowEntry.getValue().entrySet().iterator();
|
||||||
|
}
|
||||||
|
final Map.Entry<C, V> columnEntry = columnIterator.next();
|
||||||
|
return new SimpleCell<>(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
columnIterator.remove();
|
||||||
|
if (rowEntry.getValue().isEmpty()) {
|
||||||
|
rowIterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单{@link Cell} 实现
|
||||||
|
*
|
||||||
|
* @param <R> 行类型
|
||||||
|
* @param <C> 列类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
*/
|
||||||
|
private static class SimpleCell<R, C, V> implements Cell<R, C, V>, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final R rowKey;
|
||||||
|
private final C columnKey;
|
||||||
|
private final V value;
|
||||||
|
|
||||||
|
SimpleCell(R rowKey, C columnKey, V value) {
|
||||||
|
this.rowKey = rowKey;
|
||||||
|
this.columnKey = columnKey;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R getRowKey() {
|
||||||
|
return rowKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public C getColumnKey() {
|
||||||
|
return columnKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof Cell) {
|
||||||
|
Cell<?, ?, ?> other = (Cell<?, ?, ?>) obj;
|
||||||
|
return ObjectUtil.equal(rowKey, other.getRowKey())
|
||||||
|
&& ObjectUtil.equal(columnKey, other.getColumnKey())
|
||||||
|
&& ObjectUtil.equal(value, other.getValue());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(rowKey, columnKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "(" + rowKey + "," + columnKey + ")=" + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,259 @@
|
|||||||
|
package cn.hutool.core.map.multi;
|
||||||
|
|
||||||
|
import cn.hutool.core.builder.Builder;
|
||||||
|
import cn.hutool.core.collection.ComputeIter;
|
||||||
|
import cn.hutool.core.collection.IterUtil;
|
||||||
|
import cn.hutool.core.collection.TransIter;
|
||||||
|
import cn.hutool.core.map.AbsEntry;
|
||||||
|
import cn.hutool.core.map.SimpleEntry;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将行的键作为主键的{@link Table}实现<br>
|
||||||
|
* 此结构为: 行=(列=值)
|
||||||
|
*
|
||||||
|
* @param <R> 行类型
|
||||||
|
* @param <C> 列类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author Guava, Looly
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public class RowKeyTable<R, C, V> extends AbsTable<R, C, V> {
|
||||||
|
|
||||||
|
final Map<R, Map<C, V>> raw;
|
||||||
|
/**
|
||||||
|
* 列的Map创建器,用于定义Table中Value对应Map类型
|
||||||
|
*/
|
||||||
|
final Builder<? extends Map<C, V>> columnBuilder;
|
||||||
|
|
||||||
|
//region 构造
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
|
public RowKeyTable() {
|
||||||
|
this(new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param raw 原始Map
|
||||||
|
*/
|
||||||
|
public RowKeyTable(Map<R, Map<C, V>> raw) {
|
||||||
|
this(raw, HashMap::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param raw 原始Map
|
||||||
|
* @param columnMapBuilder 列的map创建器
|
||||||
|
*/
|
||||||
|
public RowKeyTable(Map<R, Map<C, V>> raw, Builder<? extends Map<C, V>> columnMapBuilder) {
|
||||||
|
this.raw = raw;
|
||||||
|
this.columnBuilder = null == columnMapBuilder ? HashMap::new : columnMapBuilder;
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<R, Map<C, V>> rowMap() {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V put(R rowKey, C columnKey, V value) {
|
||||||
|
return raw.computeIfAbsent(rowKey, (key) -> columnBuilder.build()).put(columnKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(R rowKey, C columnKey) {
|
||||||
|
final Map<C, V> map = getRow(rowKey);
|
||||||
|
if (null == map) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final V value = map.remove(columnKey);
|
||||||
|
if (map.isEmpty()) {
|
||||||
|
raw.remove(rowKey);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return raw.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
this.raw.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsColumn(C columnKey) {
|
||||||
|
if (columnKey == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Map<C, V> map : raw.values()) {
|
||||||
|
if (null != map && map.containsKey(columnKey)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//region columnMap
|
||||||
|
@Override
|
||||||
|
public Map<C, Map<R, V>> columnMap() {
|
||||||
|
Map<C, Map<R, V>> result = columnMap;
|
||||||
|
return (result == null) ? columnMap = new ColumnMap() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<C, Map<R, V>> columnMap;
|
||||||
|
|
||||||
|
private class ColumnMap extends AbstractMap<C, Map<R, V>> {
|
||||||
|
@Override
|
||||||
|
public Set<Entry<C, Map<R, V>>> entrySet() {
|
||||||
|
return new ColumnMapEntrySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ColumnMapEntrySet extends AbstractSet<Map.Entry<C, Map<R, V>>> {
|
||||||
|
private final Set<C> columnKeySet = columnKeySet();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Map.Entry<C, Map<R, V>>> iterator() {
|
||||||
|
return new TransIter<>(columnKeySet.iterator(),
|
||||||
|
c -> new SimpleEntry<>(c, getColumn(c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return columnKeySet.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
//region columnKeySet
|
||||||
|
@Override
|
||||||
|
public Set<C> columnKeySet() {
|
||||||
|
Set<C> result = columnKeySet;
|
||||||
|
return (result == null) ? columnKeySet = new ColumnKeySet() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<C> columnKeySet;
|
||||||
|
|
||||||
|
private class ColumnKeySet extends AbstractSet<C> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<C> iterator() {
|
||||||
|
return new ColumnKeyIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return IterUtil.size(iterator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ColumnKeyIterator extends ComputeIter<C> {
|
||||||
|
final Map<C, V> seen = columnBuilder.build();
|
||||||
|
final Iterator<Map<C, V>> mapIterator = raw.values().iterator();
|
||||||
|
Iterator<Map.Entry<C, V>> entryIterator = IterUtil.empty();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected C computeNext() {
|
||||||
|
while (true) {
|
||||||
|
if (entryIterator.hasNext()) {
|
||||||
|
Map.Entry<C, V> entry = entryIterator.next();
|
||||||
|
if (false == seen.containsKey(entry.getKey())) {
|
||||||
|
seen.put(entry.getKey(), entry.getValue());
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
} else if (mapIterator.hasNext()) {
|
||||||
|
entryIterator = mapIterator.next().entrySet().iterator();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region getColumn
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<R, V> getColumn(C columnKey) {
|
||||||
|
return new Column(columnKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Column extends AbstractMap<R, V> {
|
||||||
|
final C columnKey;
|
||||||
|
|
||||||
|
Column(C columnKey) {
|
||||||
|
this.columnKey = columnKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<R, V>> entrySet() {
|
||||||
|
return new EntrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EntrySet extends AbstractSet<Map.Entry<R, V>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Map.Entry<R, V>> iterator() {
|
||||||
|
return new EntrySetIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
int size = 0;
|
||||||
|
for (Map<C, V> map : raw.values()) {
|
||||||
|
if (map.containsKey(columnKey)) {
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EntrySetIterator extends ComputeIter<Entry<R, V>> {
|
||||||
|
final Iterator<Entry<R, Map<C, V>>> iterator = raw.entrySet().iterator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Entry<R, V> computeNext() {
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
final Entry<R, Map<C, V>> entry = iterator.next();
|
||||||
|
if (entry.getValue().containsKey(columnKey)) {
|
||||||
|
return new AbsEntry<R, V>() {
|
||||||
|
@Override
|
||||||
|
public R getKey() {
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getValue() {
|
||||||
|
return entry.getValue().get(columnKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V setValue(V value) {
|
||||||
|
return entry.getValue().put(columnKey, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
}
|
259
hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java
Normal file
259
hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
package cn.hutool.core.map.multi;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Opt;
|
||||||
|
import cn.hutool.core.lang.func.Consumer3;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表格数据结构定义<br>
|
||||||
|
* 此结构类似于Guava的Table接口,使用两个键映射到一个值,类似于表格结构。
|
||||||
|
*
|
||||||
|
* @param <R> 行键类型
|
||||||
|
* @param <C> 列键类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public interface Table<R, C, V> extends Iterable<Table.Cell<R, C, V>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否包含指定行列的映射<br>
|
||||||
|
* 行和列任意一个不存在都会返回{@code false},如果行和列都存在,值为{@code null},也会返回{@code true}
|
||||||
|
*
|
||||||
|
* @param rowKey 行键
|
||||||
|
* @param columnKey 列键
|
||||||
|
* @return 是否包含映射
|
||||||
|
*/
|
||||||
|
default boolean contains(R rowKey, C columnKey) {
|
||||||
|
return Opt.ofNullable(getRow(rowKey)).map((map) -> map.containsKey(columnKey)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
//region Row
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行是否存在
|
||||||
|
*
|
||||||
|
* @param rowKey 行键
|
||||||
|
* @return 行是否存在
|
||||||
|
*/
|
||||||
|
default boolean containsRow(R rowKey) {
|
||||||
|
return Opt.ofNullable(rowMap()).map((map) -> map.containsKey(rowKey)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取行
|
||||||
|
*
|
||||||
|
* @param rowKey 行键
|
||||||
|
* @return 行映射,返回的键为列键,值为表格的值
|
||||||
|
*/
|
||||||
|
default Map<C, V> getRow(R rowKey) {
|
||||||
|
return Opt.ofNullable(rowMap()).map((map) -> map.get(rowKey)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回所有行的key,行的key不可重复
|
||||||
|
*
|
||||||
|
* @return 行键
|
||||||
|
*/
|
||||||
|
default Set<R> rowKeySet() {
|
||||||
|
return Opt.ofNullable(rowMap()).map(Map::keySet).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回行列对应的Map
|
||||||
|
*
|
||||||
|
* @return map,键为行键,值为列和值的对应map
|
||||||
|
*/
|
||||||
|
Map<R, Map<C, V>> rowMap();
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region Column
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列是否存在
|
||||||
|
*
|
||||||
|
* @param columnKey 列键
|
||||||
|
* @return 列是否存在
|
||||||
|
*/
|
||||||
|
default boolean containsColumn(C columnKey) {
|
||||||
|
return Opt.ofNullable(columnMap()).map((map) -> map.containsKey(columnKey)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取列
|
||||||
|
*
|
||||||
|
* @param columnKey 列键
|
||||||
|
* @return 列映射,返回的键为行键,值为表格的值
|
||||||
|
*/
|
||||||
|
default Map<R, V> getColumn(C columnKey) {
|
||||||
|
return Opt.ofNullable(columnMap()).map((map) -> map.get(columnKey)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回所有列的key,列的key不可重复
|
||||||
|
*
|
||||||
|
* @return 列set
|
||||||
|
*/
|
||||||
|
default Set<C> columnKeySet() {
|
||||||
|
return Opt.ofNullable(columnMap()).map(Map::keySet).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回列-行对应的map
|
||||||
|
*
|
||||||
|
* @return map,键为列键,值为行和值的对应map
|
||||||
|
*/
|
||||||
|
Map<C, Map<R, V>> columnMap();
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region value
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定值是否存在
|
||||||
|
*
|
||||||
|
* @param value 值
|
||||||
|
* @return 值
|
||||||
|
*/
|
||||||
|
default boolean containsValue(V value){
|
||||||
|
final Collection<Map<C, V>> rows = Opt.ofNullable(rowMap()).map(Map::values).get();
|
||||||
|
if(null != rows){
|
||||||
|
for (Map<C, V> row : rows) {
|
||||||
|
if (row.containsValue(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定值
|
||||||
|
*
|
||||||
|
* @param rowKey 行键
|
||||||
|
* @param columnKey 列键
|
||||||
|
* @return 值,如果值不存在,返回{@code null}
|
||||||
|
*/
|
||||||
|
default V get(R rowKey, C columnKey) {
|
||||||
|
return Opt.ofNullable(getRow(rowKey)).map((map) -> map.get(columnKey)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有行列值的集合
|
||||||
|
*
|
||||||
|
* @return 值的集合
|
||||||
|
*/
|
||||||
|
Collection<V> values();
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有单元格集合
|
||||||
|
*
|
||||||
|
* @return 单元格集合
|
||||||
|
*/
|
||||||
|
Set<Cell<R, C, V>> cellSet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为表格指定行列赋值,如果不存在,创建之,存在则替换之,返回原值
|
||||||
|
*
|
||||||
|
* @param rowKey 行键
|
||||||
|
* @param columnKey 列键
|
||||||
|
* @param value 值
|
||||||
|
* @return 原值,不存在返回{@code null}
|
||||||
|
*/
|
||||||
|
V put(R rowKey, C columnKey, V value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量加入
|
||||||
|
*
|
||||||
|
* @param table 其他table
|
||||||
|
*/
|
||||||
|
default void putAll(Table<? extends R, ? extends C, ? extends V> table){
|
||||||
|
if (null != table) {
|
||||||
|
for (Table.Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
|
||||||
|
put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除指定值
|
||||||
|
*
|
||||||
|
* @param rowKey 行键
|
||||||
|
* @param columnKey 列键
|
||||||
|
* @return 移除的值,如果值不存在,返回{@code null}
|
||||||
|
*/
|
||||||
|
V remove(R rowKey, C columnKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表格是否为空
|
||||||
|
*
|
||||||
|
* @return 是否为空
|
||||||
|
*/
|
||||||
|
boolean isEmpty();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表格大小,一般为单元格的个数
|
||||||
|
*
|
||||||
|
* @return 表格大小
|
||||||
|
*/
|
||||||
|
default int size(){
|
||||||
|
final Map<R, Map<C, V>> rowMap = rowMap();
|
||||||
|
if(MapUtil.isEmpty(rowMap)){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int size = 0;
|
||||||
|
for (Map<C, V> map : rowMap.values()) {
|
||||||
|
size += map.size();
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空表格
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 遍历表格的单元格,处理值
|
||||||
|
*
|
||||||
|
* @param consumer 单元格值处理器
|
||||||
|
*/
|
||||||
|
default void forEach(Consumer3<? super R, ? super C, ? super V> consumer) {
|
||||||
|
for (Cell<R, C, V> cell : this) {
|
||||||
|
consumer.accept(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单元格,用于表示一个单元格的行、列和值
|
||||||
|
*
|
||||||
|
* @param <R> 行键类型
|
||||||
|
* @param <C> 列键类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
*/
|
||||||
|
interface Cell<R, C, V> {
|
||||||
|
/**
|
||||||
|
* 获取行键
|
||||||
|
*
|
||||||
|
* @return 行键
|
||||||
|
*/
|
||||||
|
R getRowKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取列键
|
||||||
|
*
|
||||||
|
* @return 列键
|
||||||
|
*/
|
||||||
|
C getColumnKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取值
|
||||||
|
*
|
||||||
|
* @return 值
|
||||||
|
*/
|
||||||
|
V getValue();
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* 列表类型值的Map实现
|
* 多参数类型的Map实现,包括集合类型值的Map和Table
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,8 @@ import java.io.Serializable;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可复用的字符串生成器,非线程安全
|
* 可复用的字符串生成器,非线程安全<br>
|
||||||
|
* TODO 6.x移除此类,java8的StringBuilder非常完善了,无需重写。
|
||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
|
@ -20,35 +20,60 @@ public class ModifierUtil {
|
|||||||
* @since 4.0.5
|
* @since 4.0.5
|
||||||
*/
|
*/
|
||||||
public enum ModifierType {
|
public enum ModifierType {
|
||||||
/** public修饰符,所有类都能访问 */
|
/**
|
||||||
|
* public修饰符,所有类都能访问
|
||||||
|
*/
|
||||||
PUBLIC(Modifier.PUBLIC),
|
PUBLIC(Modifier.PUBLIC),
|
||||||
/** private修饰符,只能被自己访问和修改 */
|
/**
|
||||||
|
* private修饰符,只能被自己访问和修改
|
||||||
|
*/
|
||||||
PRIVATE(Modifier.PRIVATE),
|
PRIVATE(Modifier.PRIVATE),
|
||||||
/** protected修饰符,自身、子类及同一个包中类可以访问 */
|
/**
|
||||||
|
* protected修饰符,自身、子类及同一个包中类可以访问
|
||||||
|
*/
|
||||||
PROTECTED(Modifier.PROTECTED),
|
PROTECTED(Modifier.PROTECTED),
|
||||||
/** static修饰符,(静态修饰符)指定变量被所有对象共享,即所有实例都可以使用该变量。变量属于这个类 */
|
/**
|
||||||
|
* static修饰符,(静态修饰符)指定变量被所有对象共享,即所有实例都可以使用该变量。变量属于这个类
|
||||||
|
*/
|
||||||
STATIC(Modifier.STATIC),
|
STATIC(Modifier.STATIC),
|
||||||
/** final修饰符,最终修饰符,指定此变量的值不能变,使用在方法上表示不能被重载 */
|
/**
|
||||||
|
* final修饰符,最终修饰符,指定此变量的值不能变,使用在方法上表示不能被重载
|
||||||
|
*/
|
||||||
FINAL(Modifier.FINAL),
|
FINAL(Modifier.FINAL),
|
||||||
/** synchronized,同步修饰符,在多个线程中,该修饰符用于在运行前,对他所属的方法加锁,以防止其他线程的访问,运行结束后解锁。 */
|
/**
|
||||||
|
* synchronized,同步修饰符,在多个线程中,该修饰符用于在运行前,对他所属的方法加锁,以防止其他线程的访问,运行结束后解锁。
|
||||||
|
*/
|
||||||
SYNCHRONIZED(Modifier.SYNCHRONIZED),
|
SYNCHRONIZED(Modifier.SYNCHRONIZED),
|
||||||
/** (易失修饰符)指定该变量可以同时被几个线程控制和修改 */
|
/**
|
||||||
|
* (易失修饰符)指定该变量可以同时被几个线程控制和修改
|
||||||
|
*/
|
||||||
VOLATILE(Modifier.VOLATILE),
|
VOLATILE(Modifier.VOLATILE),
|
||||||
/** (过度修饰符)指定该变量是系统保留,暂无特别作用的临时性变量,序列化时忽略 */
|
/**
|
||||||
|
* (过度修饰符)指定该变量是系统保留,暂无特别作用的临时性变量,序列化时忽略
|
||||||
|
*/
|
||||||
TRANSIENT(Modifier.TRANSIENT),
|
TRANSIENT(Modifier.TRANSIENT),
|
||||||
/** native,本地修饰符。指定此方法的方法体是用其他语言在程序外部编写的。 */
|
/**
|
||||||
|
* native,本地修饰符。指定此方法的方法体是用其他语言在程序外部编写的。
|
||||||
|
*/
|
||||||
NATIVE(Modifier.NATIVE),
|
NATIVE(Modifier.NATIVE),
|
||||||
|
|
||||||
/** abstract,将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现。 */
|
/**
|
||||||
|
* abstract,将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现。
|
||||||
|
*/
|
||||||
ABSTRACT(Modifier.ABSTRACT),
|
ABSTRACT(Modifier.ABSTRACT),
|
||||||
/** strictfp,一旦使用了关键字strictfp来声明某个类、接口或者方法时,那么在这个关键字所声明的范围内所有浮点运算都是精确的,符合IEEE-754规范的。 */
|
/**
|
||||||
|
* strictfp,一旦使用了关键字strictfp来声明某个类、接口或者方法时,那么在这个关键字所声明的范围内所有浮点运算都是精确的,符合IEEE-754规范的。
|
||||||
|
*/
|
||||||
STRICT(Modifier.STRICT);
|
STRICT(Modifier.STRICT);
|
||||||
|
|
||||||
/** 修饰符枚举对应的int修饰符值 */
|
/**
|
||||||
|
* 修饰符枚举对应的int修饰符值
|
||||||
|
*/
|
||||||
private final int value;
|
private final int value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
|
*
|
||||||
* @param modifier 修饰符int表示,见{@link Modifier}
|
* @param modifier 修饰符int表示,见{@link Modifier}
|
||||||
*/
|
*/
|
||||||
ModifierType(int modifier) {
|
ModifierType(int modifier) {
|
||||||
@ -57,6 +82,7 @@ public class ModifierUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取修饰符枚举对应的int修饰符值,值见{@link Modifier}
|
* 获取修饰符枚举对应的int修饰符值,值见{@link Modifier}
|
||||||
|
*
|
||||||
* @return 修饰符枚举对应的int修饰符值
|
* @return 修饰符枚举对应的int修饰符值
|
||||||
*/
|
*/
|
||||||
public int getValue() {
|
public int getValue() {
|
||||||
@ -67,7 +93,7 @@ public class ModifierUtil {
|
|||||||
/**
|
/**
|
||||||
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
|
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
|
||||||
*
|
*
|
||||||
* @param clazz 类
|
* @param clazz 类
|
||||||
* @param modifierTypes 修饰符枚举
|
* @param modifierTypes 修饰符枚举
|
||||||
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
|
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
|
||||||
*/
|
*/
|
||||||
@ -81,7 +107,7 @@ public class ModifierUtil {
|
|||||||
/**
|
/**
|
||||||
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
|
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
|
||||||
*
|
*
|
||||||
* @param constructor 构造方法
|
* @param constructor 构造方法
|
||||||
* @param modifierTypes 修饰符枚举
|
* @param modifierTypes 修饰符枚举
|
||||||
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
|
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
|
||||||
*/
|
*/
|
||||||
@ -95,7 +121,7 @@ public class ModifierUtil {
|
|||||||
/**
|
/**
|
||||||
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
|
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
|
||||||
*
|
*
|
||||||
* @param method 方法
|
* @param method 方法
|
||||||
* @param modifierTypes 修饰符枚举
|
* @param modifierTypes 修饰符枚举
|
||||||
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
|
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
|
||||||
*/
|
*/
|
||||||
@ -109,7 +135,7 @@ public class ModifierUtil {
|
|||||||
/**
|
/**
|
||||||
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
|
* 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true)
|
||||||
*
|
*
|
||||||
* @param field 字段
|
* @param field 字段
|
||||||
* @param modifierTypes 修饰符枚举
|
* @param modifierTypes 修饰符枚举
|
||||||
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
|
* @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false
|
||||||
*/
|
*/
|
||||||
@ -226,15 +252,27 @@ public class ModifierUtil {
|
|||||||
return clazz.isSynthetic();
|
return clazz.isSynthetic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否抽象方法
|
||||||
|
*
|
||||||
|
* @param method 方法
|
||||||
|
* @return 是否抽象方法
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public static boolean isAbstract(Method method) {
|
||||||
|
return hasModifier(method, ModifierType.ABSTRACT);
|
||||||
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------- Private method start
|
//-------------------------------------------------------------------------------------------------------- Private method start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多个修饰符做“与”操作,表示同时存在多个修饰符
|
* 多个修饰符做“与”操作,表示同时存在多个修饰符
|
||||||
|
*
|
||||||
* @param modifierTypes 修饰符列表,元素不能为空
|
* @param modifierTypes 修饰符列表,元素不能为空
|
||||||
* @return “与”之后的修饰符
|
* @return “与”之后的修饰符
|
||||||
*/
|
*/
|
||||||
private static int modifiersToInt(ModifierType... modifierTypes) {
|
private static int modifiersToInt(ModifierType... modifierTypes) {
|
||||||
int modifier = modifierTypes[0].getValue();
|
int modifier = modifierTypes[0].getValue();
|
||||||
for(int i = 1; i < modifierTypes.length; i++) {
|
for (int i = 1; i < modifierTypes.length; i++) {
|
||||||
modifier |= modifierTypes[i].getValue();
|
modifier |= modifierTypes[i].getValue();
|
||||||
}
|
}
|
||||||
return modifier;
|
return modifier;
|
||||||
|
@ -3,6 +3,7 @@ package cn.hutool.core.util;
|
|||||||
import cn.hutool.core.annotation.Alias;
|
import cn.hutool.core.annotation.Alias;
|
||||||
import cn.hutool.core.bean.NullWrapperBean;
|
import cn.hutool.core.bean.NullWrapperBean;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.collection.UniqueKeySet;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.exceptions.UtilException;
|
import cn.hutool.core.exceptions.UtilException;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
@ -17,6 +18,7 @@ import java.lang.reflect.Field;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.AbstractMap;
|
import java.util.AbstractMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -347,11 +349,12 @@ public class ReflectUtil {
|
|||||||
/**
|
/**
|
||||||
* 是否为父类引用字段<br>
|
* 是否为父类引用字段<br>
|
||||||
* 当字段所在类是对象子类时(对象中定义的非static的class),会自动生成一个以"this$0"为名称的字段,指向父类对象
|
* 当字段所在类是对象子类时(对象中定义的非static的class),会自动生成一个以"this$0"为名称的字段,指向父类对象
|
||||||
|
*
|
||||||
* @param field 字段
|
* @param field 字段
|
||||||
* @return 是否为父类引用字段
|
* @return 是否为父类引用字段
|
||||||
* @since 5.7.20
|
* @since 5.7.20
|
||||||
*/
|
*/
|
||||||
public static boolean isOuterClassField(Field field){
|
public static boolean isOuterClassField(Field field) {
|
||||||
return "this$0".equals(field.getName());
|
return "this$0".equals(field.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,51 +651,47 @@ public class ReflectUtil {
|
|||||||
*/
|
*/
|
||||||
public static Method[] getMethods(Class<?> beanClass) throws SecurityException {
|
public static Method[] getMethods(Class<?> beanClass) throws SecurityException {
|
||||||
Assert.notNull(beanClass);
|
Assert.notNull(beanClass);
|
||||||
return METHODS_CACHE.get(beanClass, () -> getMethodsDirectly(beanClass, true));
|
return METHODS_CACHE.get(beanClass,
|
||||||
|
() -> getMethodsDirectly(beanClass, true, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得一个类中所有方法列表,直接反射获取,无缓存
|
* 获得一个类中所有方法列表,直接反射获取,无缓存<br>
|
||||||
|
* 接口获取方法和默认方法,获取的方法包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>本类中的所有方法(包括static方法)</li>
|
||||||
|
* <li>父类中的所有方法(包括static方法)</li>
|
||||||
|
* <li>Object中(包括static方法)</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param beanClass 类
|
* @param beanClass 类或接口
|
||||||
* @param withSuperClassMethods 是否包括父类的方法列表
|
* @param withSupers 是否包括父类或接口的方法列表
|
||||||
|
* @param withMethodFromObject 是否包括Object中的方法
|
||||||
* @return 方法列表
|
* @return 方法列表
|
||||||
* @throws SecurityException 安全检查异常
|
* @throws SecurityException 安全检查异常
|
||||||
*/
|
*/
|
||||||
public static Method[] getMethodsDirectly(Class<?> beanClass, boolean withSuperClassMethods) throws SecurityException {
|
public static Method[] getMethodsDirectly(Class<?> beanClass, boolean withSupers, boolean withMethodFromObject) throws SecurityException {
|
||||||
Assert.notNull(beanClass);
|
Assert.notNull(beanClass);
|
||||||
|
|
||||||
Method[] allMethods = null;
|
if (beanClass.isInterface()) {
|
||||||
Class<?> searchType = beanClass;
|
// 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法
|
||||||
Class<?>[] tempInterfaceType, interfaceType;
|
return withSupers ? beanClass.getMethods() : beanClass.getDeclaredMethods();
|
||||||
Method[] declaredMethods;
|
|
||||||
if (searchType.isInterface()) {
|
|
||||||
allMethods = searchType.getDeclaredMethods();
|
|
||||||
interfaceType = searchType.getInterfaces();
|
|
||||||
while (ArrayUtil.isNotEmpty(interfaceType)) {
|
|
||||||
tempInterfaceType = interfaceType;
|
|
||||||
for (int i = 0; i < tempInterfaceType.length; i++) {
|
|
||||||
Class<?> temp = tempInterfaceType[i];
|
|
||||||
allMethods = ArrayUtil.append(allMethods, temp.getDeclaredMethods());
|
|
||||||
if (i == 0 || ArrayUtil.isEmpty(interfaceType)) {
|
|
||||||
interfaceType = temp.getInterfaces();
|
|
||||||
} else {
|
|
||||||
interfaceType = ArrayUtil.append(interfaceType, temp.getInterfaces());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (searchType != null) {
|
|
||||||
declaredMethods = searchType.getDeclaredMethods();
|
|
||||||
if (null == allMethods) {
|
|
||||||
allMethods = declaredMethods;
|
|
||||||
} else {
|
|
||||||
allMethods = ArrayUtil.append(allMethods, declaredMethods);
|
|
||||||
}
|
|
||||||
searchType = withSuperClassMethods ? searchType.getSuperclass() : null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return allMethods;
|
|
||||||
|
final UniqueKeySet<String, Method> result = new UniqueKeySet<>(true, ReflectUtil::getUniqueKey);
|
||||||
|
Class<?> searchType = beanClass;
|
||||||
|
while (searchType != null) {
|
||||||
|
if (false == withMethodFromObject && Object.class == searchType) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result.addAllIfAbsent(Arrays.asList(searchType.getDeclaredMethods()));
|
||||||
|
result.addAllIfAbsent(getDefaultMethodsFromInterface(searchType));
|
||||||
|
|
||||||
|
|
||||||
|
searchType = (withSupers && false == searchType.isInterface()) ? searchType.getSuperclass() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toArray(new Method[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -788,10 +787,10 @@ public class ReflectUtil {
|
|||||||
|
|
||||||
String name = method.getName();
|
String name = method.getName();
|
||||||
// 跳过getClass这个特殊方法
|
// 跳过getClass这个特殊方法
|
||||||
if("getClass".equals(name)){
|
if ("getClass".equals(name)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(ignoreCase){
|
if (ignoreCase) {
|
||||||
name = name.toLowerCase();
|
name = name.toLowerCase();
|
||||||
}
|
}
|
||||||
switch (parameterCount) {
|
switch (parameterCount) {
|
||||||
@ -1055,4 +1054,47 @@ public class ReflectUtil {
|
|||||||
}
|
}
|
||||||
return accessibleObject;
|
return accessibleObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取方法的唯一键,结构为:
|
||||||
|
* <pre>
|
||||||
|
* 返回类型#方法名:参数1类型,参数2类型...
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param method 方法
|
||||||
|
* @return 方法唯一键
|
||||||
|
*/
|
||||||
|
private static String getUniqueKey(Method method) {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(method.getReturnType().getName()).append('#');
|
||||||
|
sb.append(method.getName());
|
||||||
|
Class<?>[] parameters = method.getParameterTypes();
|
||||||
|
for (int i = 0; i < parameters.length; i++) {
|
||||||
|
if (i == 0) {
|
||||||
|
sb.append(':');
|
||||||
|
} else {
|
||||||
|
sb.append(',');
|
||||||
|
}
|
||||||
|
sb.append(parameters[i].getName());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类对应接口中的非抽象方法(default方法)
|
||||||
|
*
|
||||||
|
* @param clazz 类
|
||||||
|
* @return 方法列表
|
||||||
|
*/
|
||||||
|
private static List<Method> getDefaultMethodsFromInterface(Class<?> clazz) {
|
||||||
|
List<Method> result = new ArrayList<>();
|
||||||
|
for (Class<?> ifc : clazz.getInterfaces()) {
|
||||||
|
for (Method m : ifc.getMethods()) {
|
||||||
|
if (false == ModifierUtil.isAbstract(m)) {
|
||||||
|
result.add(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import cn.hutool.core.bean.copier.CopyOptions;
|
|||||||
import cn.hutool.core.bean.copier.ValueProvider;
|
import cn.hutool.core.bean.copier.ValueProvider;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.ListUtil;
|
import cn.hutool.core.collection.ListUtil;
|
||||||
|
import cn.hutool.core.map.MapBuilder;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.thread.ThreadUtil;
|
import cn.hutool.core.thread.ThreadUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
@ -78,14 +79,15 @@ public class BeanUtilTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void fillBeanWithMapIgnoreCaseTest() {
|
public void fillBeanWithMapIgnoreCaseTest() {
|
||||||
HashMap<String, Object> map = MapUtil.newHashMap();
|
Map<String, Object> map = MapBuilder.<String, Object>create()
|
||||||
map.put("Name", "Joe");
|
.put("Name", "Joe")
|
||||||
map.put("aGe", 12);
|
.put("aGe", 12)
|
||||||
map.put("openId", "DFDFSDFWERWER");
|
.put("openId", "DFDFSDFWERWER")
|
||||||
|
.build();
|
||||||
SubPerson person = BeanUtil.fillBeanWithMapIgnoreCase(map, new SubPerson(), false);
|
SubPerson person = BeanUtil.fillBeanWithMapIgnoreCase(map, new SubPerson(), false);
|
||||||
Assert.assertEquals(person.getName(), "Joe");
|
Assert.assertEquals("Joe", person.getName());
|
||||||
Assert.assertEquals(person.getAge(), 12);
|
Assert.assertEquals(12, person.getAge());
|
||||||
Assert.assertEquals(person.getOpenid(), "DFDFSDFWERWER");
|
Assert.assertEquals("DFDFSDFWERWER", person.getOpenid());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package cn.hutool.core.collection;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Console;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class UniqueKeySetTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addTest(){
|
||||||
|
Set<UniqueTestBean> set = new UniqueKeySet<>(UniqueTestBean::getId);
|
||||||
|
set.add(new UniqueTestBean("id1", "张三", "地球"));
|
||||||
|
set.add(new UniqueTestBean("id2", "李四", "火星"));
|
||||||
|
// id重复,替换之前的元素
|
||||||
|
set.add(new UniqueTestBean("id2", "王五", "木星"));
|
||||||
|
|
||||||
|
// 后两个ID重复
|
||||||
|
Assert.assertEquals(2, set.size());
|
||||||
|
|
||||||
|
set.forEach(Console::log);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
static class UniqueTestBean{
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
private String address;
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||||
import java.util.concurrent.atomic.AtomicLongArray;
|
import java.util.concurrent.atomic.AtomicLongArray;
|
||||||
|
import java.util.concurrent.atomic.DoubleAdder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类型转换工具单元测试
|
* 类型转换工具单元测试
|
||||||
@ -361,4 +362,25 @@ public class ConvertTest {
|
|||||||
final float f = Convert.toFloat(value);
|
final float f = Convert.toFloat(value);
|
||||||
Assert.assertEquals(406.1F, f, 2);
|
Assert.assertEquals(406.1F, f, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void floatToDoubleTest(){
|
||||||
|
float a = 0.45f;
|
||||||
|
double b = Convert.toDouble(a);
|
||||||
|
Assert.assertEquals(a, b, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void floatToDoubleAddrTest(){
|
||||||
|
float a = 0.45f;
|
||||||
|
final DoubleAdder adder = Convert.convert(DoubleAdder.class, a);
|
||||||
|
Assert.assertEquals(a, adder.doubleValue(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doubleToFloatTest(){
|
||||||
|
double a = 0.45f;
|
||||||
|
float b = Convert.toFloat(a);
|
||||||
|
Assert.assertEquals(a, b, 5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package cn.hutool.core.lang.test.bean;
|
package cn.hutool.core.lang.test.bean;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author 质量过关
|
* @author 质量过关
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
public class ExamInfoDict implements Serializable {
|
public class ExamInfoDict implements Serializable {
|
||||||
private static final long serialVersionUID = 3640936499125004525L;
|
private static final long serialVersionUID = 3640936499125004525L;
|
||||||
|
|
||||||
@ -18,49 +20,7 @@ public class ExamInfoDict implements Serializable {
|
|||||||
// 试题是否作答
|
// 试题是否作答
|
||||||
private Integer answerIs;
|
private Integer answerIs;
|
||||||
|
|
||||||
public Integer getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
public Integer getId(Integer defaultValue) {
|
public Integer getId(Integer defaultValue) {
|
||||||
return this.id == null ? defaultValue : this.id;
|
return this.id == null ? defaultValue : this.id;
|
||||||
}
|
}
|
||||||
public void setId(Integer id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getExamType() {
|
|
||||||
return examType;
|
|
||||||
}
|
|
||||||
public void setExamType(Integer examType) {
|
|
||||||
this.examType = examType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getAnswerIs() {
|
|
||||||
return answerIs;
|
|
||||||
}
|
|
||||||
public void setAnswerIs(Integer answerIs) {
|
|
||||||
this.answerIs = answerIs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ExamInfoDict that = (ExamInfoDict) o;
|
|
||||||
return Objects.equals(id, that.id) && Objects.equals(examType, that.examType) && Objects.equals(answerIs, that.answerIs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(id, examType, answerIs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ExamInfoDict{" + "id=" + id + ", examType=" + examType + ", answerIs=" + answerIs + '}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package cn.hutool.core.map;
|
||||||
|
|
||||||
|
import cn.hutool.core.map.multi.RowKeyTable;
|
||||||
|
import cn.hutool.core.map.multi.Table;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class RowKeyTableTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void putGetTest(){
|
||||||
|
final Table<Integer, Integer, Integer> table = new RowKeyTable<>();
|
||||||
|
table.put(1, 2, 3);
|
||||||
|
table.put(1, 6, 4);
|
||||||
|
|
||||||
|
Assert.assertEquals(new Integer(3), table.get(1, 2));
|
||||||
|
Assert.assertNull(table.get(1, 3));
|
||||||
|
|
||||||
|
//判断row和column确定的二维点是否存在
|
||||||
|
Assert.assertTrue(table.contains(1, 2));
|
||||||
|
Assert.assertFalse(table.contains(1, 3));
|
||||||
|
|
||||||
|
//判断列
|
||||||
|
Assert.assertTrue(table.containsColumn(2));
|
||||||
|
Assert.assertFalse(table.containsColumn(3));
|
||||||
|
|
||||||
|
// 判断行
|
||||||
|
Assert.assertTrue(table.containsRow(1));
|
||||||
|
Assert.assertFalse(table.containsRow(2));
|
||||||
|
|
||||||
|
|
||||||
|
// 获取列
|
||||||
|
Map<Integer, Integer> column = table.getColumn(6);
|
||||||
|
Assert.assertEquals(1, column.size());
|
||||||
|
Assert.assertEquals(new Integer(4), column.get(1));
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ public class ReflectUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getMethodsTest() {
|
public void getMethodsTest() {
|
||||||
Method[] methods = ReflectUtil.getMethods(ExamInfoDict.class);
|
Method[] methods = ReflectUtil.getMethods(ExamInfoDict.class);
|
||||||
Assert.assertEquals(22, methods.length);
|
Assert.assertEquals(20, methods.length);
|
||||||
|
|
||||||
//过滤器测试
|
//过滤器测试
|
||||||
methods = ReflectUtil.getMethods(ExamInfoDict.class, t -> Integer.class.equals(t.getReturnType()));
|
methods = ReflectUtil.getMethods(ExamInfoDict.class, t -> Integer.class.equals(t.getReturnType()));
|
||||||
@ -35,7 +35,7 @@ public class ReflectUtilTest {
|
|||||||
//null过滤器测试
|
//null过滤器测试
|
||||||
methods = ReflectUtil.getMethods(ExamInfoDict.class, null);
|
methods = ReflectUtil.getMethods(ExamInfoDict.class, null);
|
||||||
|
|
||||||
Assert.assertEquals(22, methods.length);
|
Assert.assertEquals(20, methods.length);
|
||||||
final Method method2 = methods[0];
|
final Method method2 = methods[0];
|
||||||
Assert.assertNotNull(method2);
|
Assert.assertNotNull(method2);
|
||||||
}
|
}
|
||||||
@ -114,9 +114,9 @@ public class ReflectUtilTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void getMethodBenchTest(){
|
public void getMethodBenchTest() {
|
||||||
// 预热
|
// 预热
|
||||||
getMethod(TestBenchClass.class, false, "getH");
|
getMethodWithReturnTypeCheck(TestBenchClass.class, false, "getH");
|
||||||
|
|
||||||
final TimeInterval timer = DateUtil.timer();
|
final TimeInterval timer = DateUtil.timer();
|
||||||
timer.start();
|
timer.start();
|
||||||
@ -127,7 +127,7 @@ public class ReflectUtilTest {
|
|||||||
|
|
||||||
timer.restart();
|
timer.restart();
|
||||||
for (int i = 0; i < 100000000; i++) {
|
for (int i = 0; i < 100000000; i++) {
|
||||||
getMethod(TestBenchClass.class, false, "getH");
|
getMethodWithReturnTypeCheck(TestBenchClass.class, false, "getH");
|
||||||
}
|
}
|
||||||
Console.log(timer.interval());
|
Console.log(timer.interval());
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ public class ReflectUtilTest {
|
|||||||
private String n;
|
private String n;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Method getMethod(Class<?> clazz, boolean ignoreCase, String methodName, Class<?>... paramTypes) throws SecurityException {
|
public static Method getMethodWithReturnTypeCheck(Class<?> clazz, boolean ignoreCase, String methodName, Class<?>... paramTypes) throws SecurityException {
|
||||||
if (null == clazz || StrUtil.isBlank(methodName)) {
|
if (null == clazz || StrUtil.isBlank(methodName)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -169,4 +169,74 @@ public class ReflectUtilTest {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getMethodsFromClassExtends() {
|
||||||
|
// 继承情况下,需解决方法去重问题
|
||||||
|
Method[] methods = ReflectUtil.getMethods(C2.class);
|
||||||
|
Assert.assertEquals(15, methods.length);
|
||||||
|
|
||||||
|
// 排除Object中的方法
|
||||||
|
// 3个方法包括类
|
||||||
|
methods = ReflectUtil.getMethodsDirectly(C2.class, true, false);
|
||||||
|
Assert.assertEquals(3, methods.length);
|
||||||
|
|
||||||
|
// getA属于本类
|
||||||
|
Assert.assertEquals("public void cn.hutool.core.util.ReflectUtilTest$C2.getA()", methods[0].toString());
|
||||||
|
// getB属于父类
|
||||||
|
Assert.assertEquals("public void cn.hutool.core.util.ReflectUtilTest$C1.getB()", methods[1].toString());
|
||||||
|
// getC属于接口中的默认方法
|
||||||
|
Assert.assertEquals("public default void cn.hutool.core.util.ReflectUtilTest$TestInterface1.getC()", methods[2].toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getMethodsFromInterfaceTest() {
|
||||||
|
// 对于接口,直接调用Class.getMethods方法获取所有方法,因为接口都是public方法
|
||||||
|
// 因此此处得到包括TestInterface1、TestInterface2、TestInterface3中一共4个方法
|
||||||
|
final Method[] methods = ReflectUtil.getMethods(TestInterface3.class);
|
||||||
|
Assert.assertEquals(4, methods.length);
|
||||||
|
|
||||||
|
// 接口里,调用getMethods和getPublicMethods效果相同
|
||||||
|
final Method[] publicMethods = ReflectUtil.getPublicMethods(TestInterface3.class);
|
||||||
|
Assert.assertArrayEquals(methods, publicMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TestInterface1 {
|
||||||
|
void getA();
|
||||||
|
|
||||||
|
void getB();
|
||||||
|
|
||||||
|
default void getC() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TestInterface2 extends TestInterface1 {
|
||||||
|
@Override
|
||||||
|
void getB();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TestInterface3 extends TestInterface2 {
|
||||||
|
void get3();
|
||||||
|
}
|
||||||
|
|
||||||
|
class C1 implements TestInterface2 {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getA() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getB() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class C2 extends C1 {
|
||||||
|
@Override
|
||||||
|
public void getA() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,157 @@
|
|||||||
|
package cn.hutool.crypto.symmetric;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XXTEA(Corrected Block Tiny Encryption Algorithm)算法实现<br>
|
||||||
|
* 来自:https://github.com/xxtea/xxtea-java
|
||||||
|
*
|
||||||
|
* @author Ma Bingyao <mabingyao@gmail.com>
|
||||||
|
*/
|
||||||
|
public class XXTEA implements SymmetricEncryptor, SymmetricDecryptor, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static final int DELTA = 0x9E3779B9;
|
||||||
|
|
||||||
|
private final byte[] key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param key 密钥,16位
|
||||||
|
*/
|
||||||
|
public XXTEA(byte[] key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] encrypt(byte[] data) {
|
||||||
|
if (data.length == 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return toByteArray(encrypt(
|
||||||
|
toIntArray(data, true),
|
||||||
|
toIntArray(fixKey(key), false)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encrypt(InputStream data, OutputStream out, boolean isClose) {
|
||||||
|
IoUtil.write(out, isClose, encrypt(IoUtil.readBytes(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] decrypt(byte[] data) {
|
||||||
|
if (data.length == 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return toByteArray(decrypt(
|
||||||
|
toIntArray(data, false),
|
||||||
|
toIntArray(fixKey(key), false)), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decrypt(InputStream data, OutputStream out, boolean isClose) {
|
||||||
|
IoUtil.write(out, isClose, decrypt(IoUtil.readBytes(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//region Private Method
|
||||||
|
private static int[] encrypt(int[] v, int[] k) {
|
||||||
|
int n = v.length - 1;
|
||||||
|
|
||||||
|
if (n < 1) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
int p, q = 6 + 52 / (n + 1);
|
||||||
|
int z = v[n], y, sum = 0, e;
|
||||||
|
|
||||||
|
while (q-- > 0) {
|
||||||
|
sum = sum + DELTA;
|
||||||
|
e = sum >>> 2 & 3;
|
||||||
|
for (p = 0; p < n; p++) {
|
||||||
|
y = v[p + 1];
|
||||||
|
z = v[p] += mx(sum, y, z, p, e, k);
|
||||||
|
}
|
||||||
|
y = v[0];
|
||||||
|
z = v[n] += mx(sum, y, z, p, e, k);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] decrypt(int[] v, int[] k) {
|
||||||
|
int n = v.length - 1;
|
||||||
|
|
||||||
|
if (n < 1) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
int p, q = 6 + 52 / (n + 1);
|
||||||
|
int z, y = v[0], sum = q * DELTA, e;
|
||||||
|
|
||||||
|
while (sum != 0) {
|
||||||
|
e = sum >>> 2 & 3;
|
||||||
|
for (p = n; p > 0; p--) {
|
||||||
|
z = v[p - 1];
|
||||||
|
y = v[p] -= mx(sum, y, z, p, e, k);
|
||||||
|
}
|
||||||
|
z = v[n];
|
||||||
|
y = v[0] -= mx(sum, y, z, p, e, k);
|
||||||
|
sum = sum - DELTA;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int mx(int sum, int y, int z, int p, int e, int[] k) {
|
||||||
|
return (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] fixKey(byte[] key) {
|
||||||
|
if (key.length == 16) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
byte[] fixedkey = new byte[16];
|
||||||
|
System.arraycopy(key, 0, fixedkey, 0, Math.min(key.length, 16));
|
||||||
|
return fixedkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] toIntArray(byte[] data, boolean includeLength) {
|
||||||
|
int n = (((data.length & 3) == 0)
|
||||||
|
? (data.length >>> 2)
|
||||||
|
: ((data.length >>> 2) + 1));
|
||||||
|
int[] result;
|
||||||
|
|
||||||
|
if (includeLength) {
|
||||||
|
result = new int[n + 1];
|
||||||
|
result[n] = data.length;
|
||||||
|
} else {
|
||||||
|
result = new int[n];
|
||||||
|
}
|
||||||
|
n = data.length;
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
result[i >>> 2] |= (0x000000ff & data[i]) << ((i & 3) << 3);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] toByteArray(int[] data, boolean includeLength) {
|
||||||
|
int n = data.length << 2;
|
||||||
|
|
||||||
|
if (includeLength) {
|
||||||
|
int m = data[data.length - 1];
|
||||||
|
n -= 4;
|
||||||
|
if ((m < n - 3) || (m > n)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
n = m;
|
||||||
|
}
|
||||||
|
byte[] result = new byte[n];
|
||||||
|
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
result[i] = (byte) (data[i >>> 2] >>> ((i & 3) << 3));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package cn.hutool.crypto.test.symmetric;
|
||||||
|
|
||||||
|
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
||||||
|
import cn.hutool.crypto.symmetric.XXTEA;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TEA(Tiny Encryption Algorithm)和 XTEA算法单元测试
|
||||||
|
*/
|
||||||
|
public class TEATest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void teaTest() {
|
||||||
|
String data = "测试的加密数据 by Hutool";
|
||||||
|
|
||||||
|
// 密钥必须为128bit
|
||||||
|
final SymmetricCrypto tea = new SymmetricCrypto("TEA", "MyPassword123456".getBytes());
|
||||||
|
final byte[] encrypt = tea.encrypt(data);
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
final String decryptStr = tea.decryptStr(encrypt);
|
||||||
|
|
||||||
|
Assert.assertEquals(data, decryptStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void xteaTest() {
|
||||||
|
String data = "测试的加密数据 by Hutool";
|
||||||
|
|
||||||
|
// 密钥必须为128bit
|
||||||
|
final SymmetricCrypto tea = new SymmetricCrypto("XTEA", "MyPassword123456".getBytes());
|
||||||
|
final byte[] encrypt = tea.encrypt(data);
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
final String decryptStr = tea.decryptStr(encrypt);
|
||||||
|
|
||||||
|
Assert.assertEquals(data, decryptStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void xxteaTest() {
|
||||||
|
String data = "测试的加密数据 by Hutool";
|
||||||
|
|
||||||
|
// 密钥必须为128bit
|
||||||
|
final XXTEA tea = new XXTEA("MyPassword123456".getBytes());
|
||||||
|
final byte[] encrypt = tea.encrypt(data);
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
final String decryptStr = tea.decryptStr(encrypt);
|
||||||
|
|
||||||
|
Assert.assertEquals(data, decryptStr);
|
||||||
|
}
|
||||||
|
}
|
@ -42,11 +42,11 @@ public class ScriptRuntimeException extends RuntimeException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a <code>ScriptException</code> with message, filename and linenumber to be used in error messages.
|
* Creates a {@code ScriptException} with message, filename and linenumber to be used in error messages.
|
||||||
*
|
*
|
||||||
* @param message The string to use in the message
|
* @param message The string to use in the message
|
||||||
* @param fileName The file or resource name describing the location of a script error causing the <code>ScriptException</code> to be thrown.
|
* @param fileName The file or resource name describing the location of a script error causing the {@code ScriptException} to be thrown.
|
||||||
* @param lineNumber A line number describing the location of a script error causing the <code>ScriptException</code> to be thrown.
|
* @param lineNumber A line number describing the location of a script error causing the {@code ScriptException} to be thrown.
|
||||||
*/
|
*/
|
||||||
public ScriptRuntimeException(String message, String fileName, int lineNumber) {
|
public ScriptRuntimeException(String message, String fileName, int lineNumber) {
|
||||||
super(message);
|
super(message);
|
||||||
@ -55,7 +55,7 @@ public class ScriptRuntimeException extends RuntimeException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>ScriptException</code> constructor specifying message, filename, line number and column number.
|
* {@code ScriptException} constructor specifying message, filename, line number and column number.
|
||||||
*
|
*
|
||||||
* @param message The message.
|
* @param message The message.
|
||||||
* @param fileName The filename
|
* @param fileName The filename
|
||||||
|
Loading…
x
Reference in New Issue
Block a user