This commit is contained in:
Looly 2024-08-22 22:29:34 +08:00
parent 1176b144d7
commit c6777244a0
13 changed files with 257 additions and 35 deletions

View File

@ -14,9 +14,8 @@
* limitations under the License.
*/
package org.dromara.hutool.core.collection;
package org.dromara.hutool.core.collection.set;
import org.dromara.hutool.core.collection.set.SetFromMap;
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
import java.util.Collection;

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.collection.set;
import org.dromara.hutool.core.map.concurrent.ConcurrentLinkedHashMap;
import java.util.Collection;
/**
* 通过{@link ConcurrentLinkedHashMap}实现的线程安全HashSet
*
* @param <E> 元素类型
* @author Looly
* @since 3.1.0
*/
public class ConcurrentLinkedHashSet<E> extends SetFromMap<E> {
private static final long serialVersionUID = 7997886765361607470L;
// ----------------------------------------------------------------------------------- Constructor start
/**
* 构造
*/
public ConcurrentLinkedHashSet() {
super(new ConcurrentLinkedHashMap.Builder<E, Boolean>().build());
}
/**
* 构造<br>
* 触发因子为默认的0.75
*
* @param initialCapacity 初始大小
*/
public ConcurrentLinkedHashSet(final int initialCapacity) {
super(new ConcurrentLinkedHashMap.Builder<E, Boolean>().initialCapacity(initialCapacity).build());
}
/**
* 构造
*
* @param initialCapacity 初始大小
* @param concurrencyLevel 线程并发度
*/
public ConcurrentLinkedHashSet(final int initialCapacity, final int concurrencyLevel) {
super(new ConcurrentLinkedHashMap.Builder<E, Boolean>()
.initialCapacity(initialCapacity)
.concurrencyLevel(concurrencyLevel)
.build());
}
/**
* 从已有集合中构造
*
* @param iter {@link Iterable}
*/
public ConcurrentLinkedHashSet(final Iterable<E> iter) {
super(iter instanceof Collection ?
new ConcurrentLinkedHashMap.Builder<E, Boolean>().initialCapacity(((Collection<E>) iter).size()).build() :
new ConcurrentLinkedHashMap.Builder<E, Boolean>().build());
if (iter instanceof Collection) {
this.addAll((Collection<E>) iter);
} else {
for (final E e : iter) {
this.add(e);
}
}
}
// ----------------------------------------------------------------------------------- Constructor end
}

View File

@ -154,23 +154,23 @@ public class CompositeConverter extends RegisterConverter {
return converter.convert(type, value, defaultValue);
}
Class<T> rowType = (Class<T>) TypeUtil.getClass(type);
if (null == rowType) {
Class<T> rawType = (Class<T>) TypeUtil.getClass(type);
if (null == rawType) {
if (null != defaultValue) {
rowType = (Class<T>) defaultValue.getClass();
rawType = (Class<T>) defaultValue.getClass();
} else {
throw new ConvertException("Can not get class from type: {}", type);
}
}
// 特殊类型转换包括CollectionMap强转Array等
final T result = convertSpecial(type, rowType, value, defaultValue);
final T result = convertSpecial(type, rawType, value, defaultValue);
if (null != result) {
return result;
}
// 尝试转Bean
if (BeanUtil.isWritableBean(rowType)) {
if (BeanUtil.isWritableBean(rawType)) {
return (T) BeanConverter.INSTANCE.convert(type, value);
}
@ -193,80 +193,81 @@ public class CompositeConverter extends RegisterConverter {
*
* @param <T> 转换的目标类型转换器转换到的类型
* @param type 类型
* @param rawType 原始类型
* @param value
* @param defaultValue 默认值
* @return 转换后的值
*/
@SuppressWarnings("unchecked")
private <T> T convertSpecial(final Type type, final Class<T> rowType, final Object value, final T defaultValue) {
if (null == rowType) {
private <T> T convertSpecial(final Type type, final Class<T> rawType, final Object value, final T defaultValue) {
if (null == rawType) {
return null;
}
// 日期java.sql中的日期以及自定义日期统一处理
if (Date.class.isAssignableFrom(rowType)) {
if (Date.class.isAssignableFrom(rawType)) {
return DateConverter.INSTANCE.convert(type, value, defaultValue);
}
// 集合转换含有泛型参数不可以默认强转
if (Collection.class.isAssignableFrom(rowType)) {
if (Collection.class.isAssignableFrom(rawType)) {
return (T) CollectionConverter.INSTANCE.convert(type, value, (Collection<?>) defaultValue);
}
// Map类型含有泛型参数不可以默认强转
if (Map.class.isAssignableFrom(rowType)) {
if (Map.class.isAssignableFrom(rawType)) {
return (T) MapConverter.INSTANCE.convert(type, value, (Map<?, ?>) defaultValue);
}
// issue#I6SZYB Entry类含有泛型参数不可以默认强转
if (Map.Entry.class.isAssignableFrom(rowType)) {
if (Map.Entry.class.isAssignableFrom(rawType)) {
return (T) EntryConverter.INSTANCE.convert(type, value);
}
// 默认强转
if (rowType.isInstance(value)) {
if (rawType.isInstance(value)) {
return (T) value;
}
// 原始类型转换
if (rowType.isPrimitive()) {
if (rawType.isPrimitive()) {
return PrimitiveConverter.INSTANCE.convert(type, value, defaultValue);
}
// 数字类型转换
if (Number.class.isAssignableFrom(rowType)) {
if (Number.class.isAssignableFrom(rawType)) {
return NumberConverter.INSTANCE.convert(type, value, defaultValue);
}
// 枚举转换
if (rowType.isEnum()) {
if (rawType.isEnum()) {
return EnumConverter.INSTANCE.convert(type, value, defaultValue);
}
// 数组转换
if (rowType.isArray()) {
if (rawType.isArray()) {
return ArrayConverter.INSTANCE.convert(type, value, defaultValue);
}
// Record
if (RecordUtil.isRecord(rowType)) {
if (RecordUtil.isRecord(rawType)) {
return (T) RecordConverter.INSTANCE.convert(type, value);
}
// Kotlin Bean
if (KClassUtil.isKotlinClass(rowType)) {
if (KClassUtil.isKotlinClass(rawType)) {
return (T) KBeanConverter.INSTANCE.convert(type, value);
}
// issue#I7FQ29 Class
if ("java.lang.Class".equals(rowType.getName())) {
if ("java.lang.Class".equals(rawType.getName())) {
return (T) ClassConverter.INSTANCE.convert(type, value);
}
// 空值转空Bean
if (ObjUtil.isEmpty(value)) {
// issue#3649 空值转空对象则直接实例化
return ConstructorUtil.newInstanceIfPossible(rowType);
return ConstructorUtil.newInstanceIfPossible(rawType);
}
// 表示非需要特殊转换的对象

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.convert;
import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
import org.dromara.hutool.core.stream.StreamUtil;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Set;
/**
* 用户自定义转换器<br>
* 通过自定义实现{@link PredicateConverter}可以通过{@link PredicateConverter#test(Object)} 检查目标类型是否匹配<br>
* 如果匹配则直接转换否则使用默认转换器转换
*
* @author Looly
* @since 6.0.0
*/
public class CustomConverter implements Converter, Serializable {
private static final long serialVersionUID = 1L;
/**
* 类级的内部类也就是静态的成员式内部类该内部类的实例与外部类的实例 没有绑定关系而且只有被调用到才会装载从而实现了延迟加载
*/
private static class SingletonHolder {
/**
* 静态初始化器由JVM来保证线程安全
*/
private static final RegisterConverter INSTANCE = new RegisterConverter();
}
/**
* 获得单例的 RegisterConverter
*
* @return RegisterConverter
*/
public static RegisterConverter getInstance() {
return CustomConverter.SingletonHolder.INSTANCE;
}
/**
* 用户自定义类型转换器
*/
private volatile Set<PredicateConverter> converterSet;
@Override
public Object convert(final Type targetType, final Object value) throws ConvertException {
final Converter customConverter = getCustomConverter(targetType);
return null == customConverter ? null : customConverter.convert(targetType, value);
}
/**
* 获得匹配类型的自定义转换器
*
* @param type 类型
* @return 转换器
*/
public Converter getCustomConverter(final Type type) {
return getConverterFromSet(this.converterSet, type);
}
/**
* 登记自定义转换器
*
* @param converter 转换器
* @return ConverterRegistry
*/
public CustomConverter add(final PredicateConverter converter) {
if (null == this.converterSet) {
synchronized (this) {
if (null == this.converterSet) {
this.converterSet = new ConcurrentHashSet<>();
}
}
}
this.converterSet.add(converter);
return this;
}
/**
* 从指定集合中查找满足条件的转换器
*
* @param type 类型
* @return 转换器
*/
private static Converter getConverterFromSet(final Set<? extends PredicateConverter> converterSet, final Type type) {
return StreamUtil.of(converterSet).filter((predicate) -> predicate.test(type)).findFirst().orElse(null);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.core.convert;
import java.lang.reflect.Type;
import java.util.function.Predicate;
/**
* 断言转换器<br>
* 判断目标对象是否满足断言满足则转换否则跳过<br>
* 实现此接口同样可以不判断断言而直接转换
*
* @author Looly
* @since 6.0.0
*/
public interface PredicateConverter extends Converter, Predicate<Type> {
}

View File

@ -42,7 +42,10 @@ import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.AtomicReference;
/**
* 基于类型注册的转换器转换器默认提供一些固定的类型转换用户可调用{@link #putCustom(Type, Converter)} 注册自定义转换规则
* 基于类型注册的转换器<br>
* 即转换的目标类型和转换器一一对应转换器只针对目标类型不针对源类型<br>
* 转换器默认提供一些固定的类型转换用户可调用{@link #putCustom(Type, Converter)} 注册自定义转换规则<br>
* 注意注册的转换器要求目标类型必须一致不能是子类等
*
* @author looly
* @since 6.0.0
@ -164,7 +167,7 @@ public class RegisterConverter implements Converter, Serializable {
* 注册默认转换器
*/
private void registerDefault() {
defaultConverterMap = new SafeConcurrentHashMap<>(64);
final Map<Class<?>, Converter> defaultConverterMap = new SafeConcurrentHashMap<>(64);
// 包装类转换器
defaultConverterMap.put(Character.class, new CharacterConverter());
@ -220,5 +223,7 @@ public class RegisterConverter implements Converter, Serializable {
defaultConverterMap.put(Pair.class, PairConverter.INSTANCE);// since 6.0.0
defaultConverterMap.put(Triple.class, TripleConverter.INSTANCE);// since 6.0.0
defaultConverterMap.put(Tuple.class, TupleConverter.INSTANCE);// since 6.0.0
this.defaultConverterMap = defaultConverterMap;
}
}

View File

@ -16,7 +16,7 @@
package org.dromara.hutool.core.io.watch.watchers;
import org.dromara.hutool.core.collection.ConcurrentHashSet;
import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
import org.dromara.hutool.core.io.watch.Watcher;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.thread.ThreadUtil;

View File

@ -16,7 +16,7 @@
package org.dromara.hutool.core.stream;
import org.dromara.hutool.core.collection.ConcurrentHashSet;
import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
import org.dromara.hutool.core.collection.iter.IterUtil;
import org.dromara.hutool.core.map.multi.RowKeyTable;
import org.dromara.hutool.core.map.multi.Table;

View File

@ -12,7 +12,7 @@
package org.dromara.hutool.core.data.id;
import org.dromara.hutool.core.collection.ConcurrentHashSet;
import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
import org.dromara.hutool.core.exception.HutoolException;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.lang.tuple.Pair;

View File

@ -12,8 +12,7 @@
package org.dromara.hutool.core.data.id;
import org.dromara.hutool.core.collection.ConcurrentHashSet;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
import org.dromara.hutool.core.thread.ThreadUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

View File

@ -12,7 +12,7 @@
package org.dromara.hutool.core.util;
import org.dromara.hutool.core.collection.ConcurrentHashSet;
import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.date.StopWatch;
import org.dromara.hutool.core.exception.HutoolException;

View File

@ -20,7 +20,6 @@ import org.dromara.hutool.core.bean.path.BeanPath;
import org.dromara.hutool.core.convert.ConvertException;
import org.dromara.hutool.core.convert.Converter;
import org.dromara.hutool.core.lang.mutable.MutableEntry;
import org.dromara.hutool.json.convert.JSONConverter;
import java.io.Serializable;
import java.io.StringWriter;
@ -193,6 +192,6 @@ public interface JSON extends Converter, Cloneable, Serializable {
@Override
default Object convert(final Type targetType, final Object value) throws ConvertException {
return JSONConverter.of(config()).convert(targetType, value);
return config().getConverter().convert(targetType, value);
}
}

View File

@ -61,8 +61,9 @@ public class JSONConverter implements Converter, Serializable {
public static final JSONConverter INSTANCE = new JSONConverter(null);
static {
RegisterConverter.getInstance().putCustom(JSONObject.class, INSTANCE);
RegisterConverter.getInstance().putCustom(JSONArray.class, INSTANCE);
final RegisterConverter converter = RegisterConverter.getInstance();
converter.putCustom(JSONObject.class, INSTANCE);
converter.putCustom(JSONArray.class, INSTANCE);
}
/**