mirror of
https://gitee.com/dromara/hutool.git
synced 2025-06-28 13:34:09 +08:00
去除JDK8兼容代码
This commit is contained in:
parent
45419bb8dc
commit
07739d72db
@ -19,10 +19,10 @@ package cn.hutool.v7.ai;
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.AIService;
|
||||
import cn.hutool.v7.ai.core.AIServiceProvider;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 创建AIModelService的工厂类
|
||||
@ -32,7 +32,7 @@ import java.util.ServiceLoader;
|
||||
*/
|
||||
public class AIServiceFactory {
|
||||
|
||||
private static final Map<String, AIServiceProvider> providers = new SafeConcurrentHashMap<>();
|
||||
private static final Map<String, AIServiceProvider> providers = new ConcurrentHashMap<>();
|
||||
|
||||
// 加载所有 AIModelProvider 实现类
|
||||
static {
|
||||
|
@ -16,10 +16,9 @@
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* AIConfig实现类的加载器
|
||||
@ -29,7 +28,7 @@ import java.util.ServiceLoader;
|
||||
*/
|
||||
public class AIConfigRegistry {
|
||||
|
||||
private static final Map<String, Class<? extends AIConfig>> configClasses = new SafeConcurrentHashMap<>();
|
||||
private static final Map<String, Class<? extends AIConfig>> configClasses = new ConcurrentHashMap<>();
|
||||
|
||||
// 加载所有 AIConfig 实现类
|
||||
static {
|
||||
|
@ -16,9 +16,8 @@
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Config基础类,定义模型配置的基本属性
|
||||
@ -35,7 +34,7 @@ public class BaseConfig implements AIConfig {
|
||||
//具体模型
|
||||
protected volatile String model;
|
||||
//动态扩展字段
|
||||
protected Map<String, Object> additionalConfig = new SafeConcurrentHashMap<>();
|
||||
protected Map<String, Object> additionalConfig = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void setApiKey(final String apiKey) {
|
||||
@ -79,7 +78,7 @@ public class BaseConfig implements AIConfig {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAdditionalConfigMap() {
|
||||
return new SafeConcurrentHashMap<>(additionalConfig);
|
||||
return new ConcurrentHashMap<>(additionalConfig);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.reflect.method.MethodUtil;
|
||||
import cn.hutool.v7.core.text.CharSequenceUtil;
|
||||
|
||||
@ -28,6 +27,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@ -96,7 +96,7 @@ public final class AnnotationMappingProxy<T extends Annotation> implements Invoc
|
||||
private AnnotationMappingProxy(final AnnotationMapping<T> annotation) {
|
||||
final int methodCount = annotation.getAttributes().length;
|
||||
this.methods = new HashMap<>(methodCount + 5);
|
||||
this.valueCache = new SafeConcurrentHashMap<>(methodCount);
|
||||
this.valueCache = new ConcurrentHashMap<>(methodCount);
|
||||
this.mapping = annotation;
|
||||
loadMethods();
|
||||
}
|
||||
|
@ -17,31 +17,21 @@
|
||||
package cn.hutool.v7.core.bean;
|
||||
|
||||
import cn.hutool.v7.core.bean.copier.ValueProvider;
|
||||
import cn.hutool.v7.core.classloader.ClassLoaderUtil;
|
||||
import cn.hutool.v7.core.reflect.ConstructorUtil;
|
||||
import cn.hutool.v7.core.reflect.method.MethodUtil;
|
||||
import cn.hutool.v7.core.util.JdkUtil;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* java.lang.Record 相关工具类封装<br>
|
||||
* 来自于FastJSON2的BeanUtils
|
||||
* java.lang.Record 相关工具类封装
|
||||
*
|
||||
* @author fastjson2, Looly
|
||||
* @since 6.0.0
|
||||
* @author Looly
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public class RecordUtil {
|
||||
|
||||
private static volatile Class<?> RECORD_CLASS;
|
||||
|
||||
private static volatile Method METHOD_GET_RECORD_COMPONENTS;
|
||||
private static volatile Method METHOD_COMPONENT_GET_NAME;
|
||||
private static volatile Method METHOD_COMPONENT_GET_GENERIC_TYPE;
|
||||
|
||||
/**
|
||||
* 判断给定类是否为Record类
|
||||
*
|
||||
@ -49,27 +39,7 @@ public class RecordUtil {
|
||||
* @return 是否为Record类
|
||||
*/
|
||||
public static boolean isRecord(final Class<?> clazz) {
|
||||
if (JdkUtil.JVM_VERSION < 14) {
|
||||
// JDK14+支持Record类
|
||||
return false;
|
||||
}
|
||||
final Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (RECORD_CLASS == null) {
|
||||
// 此处不使用同步代码,重复赋值并不影响判断
|
||||
final String superclassName = superClass.getName();
|
||||
if ("java.lang.Record".equals(superclassName)) {
|
||||
RECORD_CLASS = superClass;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return superClass == RECORD_CLASS;
|
||||
return null != clazz && clazz.isRecord();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,31 +50,11 @@ public class RecordUtil {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map.Entry<String, Type>[] getRecordComponents(final Class<?> recordClass) {
|
||||
if (JdkUtil.JVM_VERSION < 14) {
|
||||
// JDK14+支持Record类
|
||||
return new Map.Entry[0];
|
||||
}
|
||||
if (null == METHOD_GET_RECORD_COMPONENTS) {
|
||||
METHOD_GET_RECORD_COMPONENTS = MethodUtil.getMethod(Class.class, "getRecordComponents");
|
||||
}
|
||||
|
||||
final Class<Object> recordComponentClass = ClassLoaderUtil.loadClass("java.lang.reflect.RecordComponent");
|
||||
if (METHOD_COMPONENT_GET_NAME == null) {
|
||||
METHOD_COMPONENT_GET_NAME = MethodUtil.getMethod(recordComponentClass, "getName");
|
||||
}
|
||||
if (METHOD_COMPONENT_GET_GENERIC_TYPE == null) {
|
||||
METHOD_COMPONENT_GET_GENERIC_TYPE = MethodUtil.getMethod(recordComponentClass, "getGenericType");
|
||||
}
|
||||
|
||||
final Object[] components = MethodUtil.invoke(recordClass, METHOD_GET_RECORD_COMPONENTS);
|
||||
final RecordComponent[] components = recordClass.getRecordComponents();
|
||||
final Map.Entry<String, Type>[] entries = new Map.Entry[components.length];
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
entries[i] = new AbstractMap.SimpleEntry<>(
|
||||
MethodUtil.invoke(components[i], METHOD_COMPONENT_GET_NAME),
|
||||
MethodUtil.invoke(components[i], METHOD_COMPONENT_GET_GENERIC_TYPE)
|
||||
);
|
||||
entries[i] = new AbstractMap.SimpleEntry<>(components[i].getName(), components[i].getGenericType());
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ import cn.hutool.v7.core.func.SerSupplier;
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.core.lang.mutable.Mutable;
|
||||
import cn.hutool.v7.core.lang.mutable.MutableObj;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.map.reference.WeakConcurrentMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -29,6 +28,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@ -57,7 +57,7 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
|
||||
/**
|
||||
* 写的时候每个key一把锁,降低锁的粒度
|
||||
*/
|
||||
protected final Map<K, Lock> keyLockMap = new SafeConcurrentHashMap<>();
|
||||
protected final Map<K, Lock> keyLockMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 构造,默认使用{@link WeakHashMap}实现缓存自动清理
|
||||
|
@ -21,11 +21,12 @@ import cn.hutool.v7.core.cache.CacheListener;
|
||||
import cn.hutool.v7.core.func.SerSupplier;
|
||||
import cn.hutool.v7.core.lang.mutable.Mutable;
|
||||
import cn.hutool.v7.core.lang.mutable.MutableObj;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@ -44,6 +45,7 @@ import java.util.stream.Collectors;
|
||||
* @author Looly, jodd
|
||||
*/
|
||||
public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
@ -54,7 +56,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
/**
|
||||
* 写的时候每个key一把锁,降低锁的粒度
|
||||
*/
|
||||
protected final Map<K, Lock> keyLockMap = new SafeConcurrentHashMap<>();
|
||||
protected final Map<K, Lock> keyLockMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 返回缓存容量,{@code 0}表示无大小限制
|
||||
|
@ -16,12 +16,12 @@
|
||||
|
||||
package cn.hutool.v7.core.collection.set;
|
||||
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 通过{@link SafeConcurrentHashMap}实现的线程安全HashSet
|
||||
* 通过{@link ConcurrentHashMap}实现的线程安全HashSet
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
@ -29,6 +29,7 @@ import java.util.Collection;
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class ConcurrentHashSet<E> extends SetFromMap<E> {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 7997886765361607470L;
|
||||
|
||||
// ----------------------------------------------------------------------------------- Constructor start
|
||||
@ -37,7 +38,7 @@ public class ConcurrentHashSet<E> extends SetFromMap<E> {
|
||||
* 触发因子为默认的0.75
|
||||
*/
|
||||
public ConcurrentHashSet() {
|
||||
super(new SafeConcurrentHashMap<>());
|
||||
super(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,7 +48,7 @@ public class ConcurrentHashSet<E> extends SetFromMap<E> {
|
||||
* @param initialCapacity 初始大小
|
||||
*/
|
||||
public ConcurrentHashSet(final int initialCapacity) {
|
||||
super(new SafeConcurrentHashMap<>(initialCapacity));
|
||||
super(new ConcurrentHashMap<>(initialCapacity));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,7 +58,7 @@ public class ConcurrentHashSet<E> extends SetFromMap<E> {
|
||||
* @param loadFactor 加载因子。此参数决定数据增长时触发的百分比
|
||||
*/
|
||||
public ConcurrentHashSet(final int initialCapacity, final float loadFactor) {
|
||||
super(new SafeConcurrentHashMap<>(initialCapacity, loadFactor));
|
||||
super(new ConcurrentHashMap<>(initialCapacity, loadFactor));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,7 +69,7 @@ public class ConcurrentHashSet<E> extends SetFromMap<E> {
|
||||
* @param concurrencyLevel 线程并发度
|
||||
*/
|
||||
public ConcurrentHashSet(final int initialCapacity, final float loadFactor, final int concurrencyLevel) {
|
||||
super(new SafeConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel));
|
||||
super(new ConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,7 +77,7 @@ public class ConcurrentHashSet<E> extends SetFromMap<E> {
|
||||
* @param iter {@link Iterable}
|
||||
*/
|
||||
public ConcurrentHashSet(final Iterable<E> iter) {
|
||||
super(iter instanceof Collection ? new SafeConcurrentHashMap<>(((Collection<E>)iter).size()) : new SafeConcurrentHashMap<>());
|
||||
super(iter instanceof Collection ? new ConcurrentHashMap<>(((Collection<E>)iter).size()) : new ConcurrentHashMap<>());
|
||||
if(iter instanceof Collection) {
|
||||
this.addAll((Collection<E>)iter);
|
||||
}else {
|
||||
|
@ -22,11 +22,11 @@ import cn.hutool.v7.core.lang.Opt;
|
||||
import cn.hutool.v7.core.lang.tuple.Pair;
|
||||
import cn.hutool.v7.core.lang.tuple.Triple;
|
||||
import cn.hutool.v7.core.lang.tuple.Tuple;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.reflect.TypeUtil;
|
||||
import cn.hutool.v7.core.stream.StreamUtil;
|
||||
|
||||
import javax.xml.datatype.XMLGregorianCalendar;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
@ -38,6 +38,7 @@ import java.nio.file.Path;
|
||||
import java.time.*;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
import java.util.concurrent.atomic.AtomicLongArray;
|
||||
@ -55,6 +56,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class RegisterConverter extends ConverterWithRoot implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
@ -169,7 +171,7 @@ public class RegisterConverter extends ConverterWithRoot implements Serializable
|
||||
if (null == customConverterMap) {
|
||||
synchronized (this) {
|
||||
if (null == customConverterMap) {
|
||||
customConverterMap = new SafeConcurrentHashMap<>();
|
||||
customConverterMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -201,7 +203,7 @@ public class RegisterConverter extends ConverterWithRoot implements Serializable
|
||||
* @return 默认转换器
|
||||
*/
|
||||
private static Map<Class<?>, Converter> initDefault(final Converter rootConverter) {
|
||||
final Map<Class<?>, Converter> converterMap = new SafeConcurrentHashMap<>(64);
|
||||
final Map<Class<?>, Converter> converterMap = new ConcurrentHashMap<>(64);
|
||||
|
||||
// 包装类转换器
|
||||
converterMap.put(Character.class, CharacterConverter.INSTANCE);
|
||||
|
@ -16,13 +16,13 @@
|
||||
|
||||
package cn.hutool.v7.core.data;
|
||||
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.regex.PatternPool;
|
||||
import cn.hutool.v7.core.regex.ReUtil;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.core.util.RandomUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@ -62,7 +62,7 @@ public class CreditCodeUtil {
|
||||
private static final Map<Character, Integer> CODE_INDEX_MAP;
|
||||
|
||||
static {
|
||||
CODE_INDEX_MAP = new SafeConcurrentHashMap<>();
|
||||
CODE_INDEX_MAP = new ConcurrentHashMap<>();
|
||||
for (int i = 0; i < BASE_CODE_ARRAY.length; i++) {
|
||||
CODE_INDEX_MAP.put(BASE_CODE_ARRAY[i], i);
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ package cn.hutool.v7.core.date.format;
|
||||
|
||||
import cn.hutool.v7.core.date.DateUtil;
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
@ -70,8 +70,8 @@ public class DateFormatManager {
|
||||
* 构造
|
||||
*/
|
||||
public DateFormatManager() {
|
||||
formatterMap = new SafeConcurrentHashMap<>();
|
||||
parserMap = new SafeConcurrentHashMap<>();
|
||||
formatterMap = new ConcurrentHashMap<>();
|
||||
parserMap = new ConcurrentHashMap<>();
|
||||
|
||||
// Hutool预设的几种自定义格式
|
||||
registerFormatter(FORMAT_SECONDS, (date) -> String.valueOf(Math.floorDiv(date.getTime(), 1000L)));
|
||||
|
@ -17,11 +17,11 @@
|
||||
package cn.hutool.v7.core.date.format;
|
||||
|
||||
import cn.hutool.v7.core.date.DateException;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
@ -296,14 +296,11 @@ public class DatePattern {
|
||||
* @return a new rule with the correct padding
|
||||
*/
|
||||
protected static NumberRule selectNumberRule(final int field, final int padding) {
|
||||
switch (padding) {
|
||||
case 1:
|
||||
return new UnpaddedNumberField(field);
|
||||
case 2:
|
||||
return new TwoDigitNumberField(field);
|
||||
default:
|
||||
return new PaddedNumberField(field, padding);
|
||||
}
|
||||
return switch (padding) {
|
||||
case 1 -> new UnpaddedNumberField(field);
|
||||
case 2 -> new TwoDigitNumberField(field);
|
||||
default -> new PaddedNumberField(field, padding);
|
||||
};
|
||||
}
|
||||
|
||||
// Rules
|
||||
@ -901,16 +898,12 @@ public class DatePattern {
|
||||
* @return a Iso8601_Rule that can format TimeZone String of length {@code tokenLen}. If no such rule exists, an IllegalArgumentException will be thrown.
|
||||
*/
|
||||
static Iso8601_Rule getRule(final int tokenLen) {
|
||||
switch (tokenLen) {
|
||||
case 1:
|
||||
return Iso8601_Rule.ISO8601_HOURS;
|
||||
case 2:
|
||||
return Iso8601_Rule.ISO8601_HOURS_MINUTES;
|
||||
case 3:
|
||||
return Iso8601_Rule.ISO8601_HOURS_COLON_MINUTES;
|
||||
default:
|
||||
throw new IllegalArgumentException("invalid number of X");
|
||||
}
|
||||
return switch (tokenLen) {
|
||||
case 1 -> Iso8601_Rule.ISO8601_HOURS;
|
||||
case 2 -> Iso8601_Rule.ISO8601_HOURS_MINUTES;
|
||||
case 3 -> Iso8601_Rule.ISO8601_HOURS_COLON_MINUTES;
|
||||
default -> throw new IllegalArgumentException("invalid number of X");
|
||||
};
|
||||
}
|
||||
|
||||
final int length;
|
||||
@ -1045,7 +1038,7 @@ public class DatePattern {
|
||||
}
|
||||
}
|
||||
|
||||
private static final ConcurrentMap<TimeZoneDisplayKey, String> C_TIME_ZONE_DISPLAY_CACHE = new SafeConcurrentHashMap<>(7);
|
||||
private static final ConcurrentMap<TimeZoneDisplayKey, String> C_TIME_ZONE_DISPLAY_CACHE = new ConcurrentHashMap<>(7);
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@ -1110,8 +1103,7 @@ public class DatePattern {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof TimeZoneDisplayKey) {
|
||||
final TimeZoneDisplayKey other = (TimeZoneDisplayKey) obj;
|
||||
if (obj instanceof TimeZoneDisplayKey other) {
|
||||
return mTimeZone.equals(other.mTimeZone) && mStyle == other.mStyle && mLocale.equals(other.mLocale);
|
||||
}
|
||||
return false;
|
||||
|
@ -18,13 +18,13 @@ package cn.hutool.v7.core.date.format;
|
||||
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.core.lang.tuple.Tuple;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.Format;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
@ -40,9 +40,9 @@ abstract class FormatCache<F extends Format> {
|
||||
*/
|
||||
static final int NONE = -1;
|
||||
|
||||
private final ConcurrentMap<Tuple, F> cInstanceCache = new SafeConcurrentHashMap<>(7);
|
||||
private final ConcurrentMap<Tuple, F> cInstanceCache = new ConcurrentHashMap<>(7);
|
||||
|
||||
private static final ConcurrentMap<Tuple, String> C_DATE_TIME_INSTANCE_CACHE = new SafeConcurrentHashMap<>(7);
|
||||
private static final ConcurrentMap<Tuple, String> C_DATE_TIME_INSTANCE_CACHE = new ConcurrentHashMap<>(7);
|
||||
|
||||
/**
|
||||
* 使用默认的pattern、timezone和locale获得缓存中的实例
|
||||
|
@ -20,14 +20,15 @@ import cn.hutool.v7.core.date.DateException;
|
||||
import cn.hutool.v7.core.date.format.FastDateFormat;
|
||||
import cn.hutool.v7.core.date.format.FastDatePrinter;
|
||||
import cn.hutool.v7.core.date.format.SimpleDateBasic;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serial;
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -40,6 +41,7 @@ import java.util.regex.Pattern;
|
||||
* @since 2.16.2
|
||||
*/
|
||||
public class FastDateParser extends SimpleDateBasic implements PositionDateParser {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -3199383897950947498L;
|
||||
|
||||
static final Locale JAPANESE_IMPERIAL = new Locale("ja", "JP", "JP");
|
||||
@ -221,6 +223,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
||||
* @throws IOException if there is an IO issue.
|
||||
* @throws ClassNotFoundException if a class cannot be found.
|
||||
*/
|
||||
@Serial
|
||||
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
|
||||
@ -380,8 +383,6 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
||||
*/
|
||||
private Strategy getStrategy(final char f, final int width, final Calendar definingCalendar) {
|
||||
switch (f) {
|
||||
default:
|
||||
throw new IllegalArgumentException("Format '" + f + "' not supported");
|
||||
case 'D':
|
||||
return DAY_OF_YEAR_STRATEGY;
|
||||
case 'E':
|
||||
@ -428,6 +429,8 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
||||
//$FALL-THROUGH$
|
||||
case 'z':
|
||||
return getLocaleSpecificStrategy(Calendar.ZONE_OFFSET, definingCalendar);
|
||||
default:
|
||||
throw new IllegalArgumentException("Format '" + f + "' not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,7 +446,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
||||
private static ConcurrentMap<Locale, Strategy> getCache(final int field) {
|
||||
synchronized (CACHES) {
|
||||
if (CACHES[field] == null) {
|
||||
CACHES[field] = new SafeConcurrentHashMap<>(3);
|
||||
CACHES[field] = new ConcurrentHashMap<>(3);
|
||||
}
|
||||
return CACHES[field];
|
||||
}
|
||||
@ -669,15 +672,14 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
||||
final TzInfo standard = new TzInfo(tz, false);
|
||||
TzInfo tzInfo = standard;
|
||||
for (int i = 1; i < zoneNames.length; ++i) {
|
||||
switch (i) {
|
||||
case 3: // offset 3 is long daylight savings (or summertime) name
|
||||
tzInfo = switch (i) {
|
||||
case 3 -> // offset 3 is long daylight savings (or summertime) name
|
||||
// offset 4 is the short summertime name
|
||||
tzInfo = new TzInfo(tz, true);
|
||||
break;
|
||||
case 5: // offset 5 starts additional names, probably standard time
|
||||
tzInfo = standard;
|
||||
break;
|
||||
}
|
||||
new TzInfo(tz, true);
|
||||
case 5 -> // offset 5 starts additional names, probably standard time
|
||||
standard;
|
||||
default -> tzInfo;
|
||||
};
|
||||
if (zoneNames[i] != null) {
|
||||
final String key = zoneNames[i].toLowerCase(locale);
|
||||
// ignore the data associated with duplicates supplied in
|
||||
@ -747,16 +749,12 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
||||
* @return a ISO8601TimeZoneStrategy that can format TimeZone String of length {@code tokenLen}. If no such strategy exists, an IllegalArgumentException will be thrown.
|
||||
*/
|
||||
static Strategy getStrategy(final int tokenLen) {
|
||||
switch (tokenLen) {
|
||||
case 1:
|
||||
return ISO_8601_1_STRATEGY;
|
||||
case 2:
|
||||
return ISO_8601_2_STRATEGY;
|
||||
case 3:
|
||||
return ISO_8601_3_STRATEGY;
|
||||
default:
|
||||
throw new IllegalArgumentException("invalid number of X");
|
||||
}
|
||||
return switch (tokenLen) {
|
||||
case 1 -> ISO_8601_1_STRATEGY;
|
||||
case 2 -> ISO_8601_2_STRATEGY;
|
||||
case 3 -> ISO_8601_3_STRATEGY;
|
||||
default -> throw new IllegalArgumentException("invalid number of X");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 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 cn.hutool.v7.core.func;
|
||||
|
||||
import cn.hutool.v7.core.reflect.ConstructorUtil;
|
||||
import cn.hutool.v7.core.util.JdkUtil;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* 常用Lambda函数封装<br>
|
||||
* 提供常用对象方法的Lambda包装,减少Lambda初始化时间。
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class FunctionPool {
|
||||
|
||||
/**
|
||||
* 通过{@code String(char[] value, boolean share)}这个内部构造生成一个Lambda函数<br>
|
||||
* 此函数通过传入char[],实现zero-copy的String创建,效率很高。但是要求传入的char[]不可以在其他地方修改。<br>
|
||||
* 此函数只支持JKDK8
|
||||
*/
|
||||
public static final BiFunction<char[], Boolean, String> STRING_CREATOR_JDK8;
|
||||
|
||||
static {
|
||||
final Constructor<String> constructor = ConstructorUtil.getConstructor(String.class, char[].class, boolean.class);
|
||||
STRING_CREATOR_JDK8 = JdkUtil.IS_JDK8 ? LambdaFactory.build(BiFunction.class, constructor) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过{@code String(char[] value, boolean share)}这个内部构造创建String对象。<br>
|
||||
* 此函数通过传入char[],实现zero-copy的String创建,效率很高。但是要求传入的char[]不可以在其他地方修改。
|
||||
*
|
||||
* @param value char[]值,注意这个数组不可修改!!
|
||||
* @return String
|
||||
*/
|
||||
public static String createString(final char[] value) {
|
||||
if(JdkUtil.IS_JDK8){
|
||||
return STRING_CREATOR_JDK8.apply(value, true);
|
||||
} else {
|
||||
// TODO JDK9+优化
|
||||
return new String(value);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,11 +18,11 @@ package cn.hutool.v7.core.lang;
|
||||
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import cn.hutool.v7.core.func.SerSupplier;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.reflect.ConstructorUtil;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -33,7 +33,7 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public final class Singleton {
|
||||
|
||||
private static final SafeConcurrentHashMap<String, Object> POOL = new SafeConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<String, Object> POOL = new ConcurrentHashMap<>();
|
||||
|
||||
private Singleton() {
|
||||
}
|
||||
|
@ -23,14 +23,12 @@ import cn.hutool.v7.core.collection.iter.ArrayIter;
|
||||
import cn.hutool.v7.core.collection.iter.IterUtil;
|
||||
import cn.hutool.v7.core.func.SerConsumer3;
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.reflect.ConstructorUtil;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.core.util.ObjUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -196,45 +194,6 @@ public class MapUtil extends MapGetUtil {
|
||||
return new IdentityHashMap<>(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建一个初始容量为{@link MapUtil#DEFAULT_INITIAL_CAPACITY} 的{@link SafeConcurrentHashMap}
|
||||
*
|
||||
* @param <K> key的类型
|
||||
* @param <V> value的类型
|
||||
* @return {@link SafeConcurrentHashMap}
|
||||
*/
|
||||
public static <K, V> ConcurrentHashMap<K, V> newSafeConcurrentHashMap() {
|
||||
return new SafeConcurrentHashMap<>(DEFAULT_INITIAL_CAPACITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建一个{@link SafeConcurrentHashMap}
|
||||
*
|
||||
* @param size 初始容量,当传入的容量小于等于0时,容量为{@link MapUtil#DEFAULT_INITIAL_CAPACITY}
|
||||
* @param <K> key的类型
|
||||
* @param <V> value的类型
|
||||
* @return {@link SafeConcurrentHashMap}
|
||||
*/
|
||||
public static <K, V> ConcurrentHashMap<K, V> newSafeConcurrentHashMap(final int size) {
|
||||
final int initCapacity = size <= 0 ? DEFAULT_INITIAL_CAPACITY : size;
|
||||
return new SafeConcurrentHashMap<>(initCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 传入一个Map将其转化为{@link SafeConcurrentHashMap}类型
|
||||
*
|
||||
* @param map map
|
||||
* @param <K> key的类型
|
||||
* @param <V> value的类型
|
||||
* @return {@link SafeConcurrentHashMap}
|
||||
*/
|
||||
public static <K, V> ConcurrentHashMap<K, V> newSafeConcurrentHashMap(final Map<K, V> map) {
|
||||
if (isEmpty(map)) {
|
||||
return new ConcurrentHashMap<>(DEFAULT_INITIAL_CAPACITY);
|
||||
}
|
||||
return new SafeConcurrentHashMap<>(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Map<br>
|
||||
* 传入抽象Map{@link AbstractMap}和{@link Map}类将默认创建{@link HashMap}
|
||||
@ -430,11 +389,9 @@ public class MapUtil extends MapGetUtil {
|
||||
final HashMap<Object, Object> map = new HashMap<>((int) (array.length * 1.5));
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
final Object object = array[i];
|
||||
if (object instanceof Map.Entry) {
|
||||
final Map.Entry entry = (Map.Entry) object;
|
||||
if (object instanceof Map.Entry entry) {
|
||||
map.put(entry.getKey(), entry.getValue());
|
||||
} else if (object instanceof Object[]) {
|
||||
final Object[] entry = (Object[]) object;
|
||||
} else if (object instanceof Object[] entry) {
|
||||
if (entry.length > 1) {
|
||||
map.put(entry[0], entry[1]);
|
||||
}
|
||||
@ -447,8 +404,7 @@ public class MapUtil extends MapGetUtil {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
} else if (object instanceof Iterator) {
|
||||
final Iterator iter = ((Iterator) object);
|
||||
} else if (object instanceof Iterator iter) {
|
||||
if (iter.hasNext()) {
|
||||
final Object key = iter.next();
|
||||
if (iter.hasNext()) {
|
||||
@ -882,9 +838,8 @@ public class MapUtil extends MapGetUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (map instanceof TreeMap) {
|
||||
if (map instanceof TreeMap<K, V> result) {
|
||||
// 已经是可排序Map,此时只有比较器一致才返回原map
|
||||
final TreeMap<K, V> result = (TreeMap<K, V>) map;
|
||||
if (null == comparator || comparator.equals(result.comparator())) {
|
||||
return result;
|
||||
}
|
||||
@ -1358,43 +1313,6 @@ public class MapUtil extends MapGetUtil {
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果 key 对应的 value 不存在,则使用获取 mappingFunction 重新计算后的值,并保存为该 key 的 value,否则返回 value。<br>
|
||||
* 解决使用ConcurrentHashMap.computeIfAbsent导致的死循环问题。(issues#2349)<br>
|
||||
* A temporary workaround for Java 8 specific performance issue JDK-8161372 .<br>
|
||||
* This class should be removed once we drop Java 8 support.
|
||||
*
|
||||
* <p>
|
||||
* 注意此方法只能用于JDK8
|
||||
* </p>
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @param map Map,一般用于线程安全的Map
|
||||
* @param key 键
|
||||
* @param mappingFunction 值计算函数
|
||||
* @return 值
|
||||
* @see <a href="https://bugs.openjdk.java.net/browse/JDK-8161372">https://bugs.openjdk.java.net/browse/JDK-8161372</a>
|
||||
*/
|
||||
public static <K, V> V computeIfAbsentForJdk8(final Map<K, V> map, final K key, final Function<? super K, ? extends V> mappingFunction) {
|
||||
V value = map.get(key);
|
||||
if (null == value) {
|
||||
value = mappingFunction.apply(key);
|
||||
final V res = map.putIfAbsent(key, value);
|
||||
if (null != res) {
|
||||
// issues#I6RVMY
|
||||
// 如果旧值存在,说明其他线程已经赋值成功,putIfAbsent没有执行,返回旧值
|
||||
return res;
|
||||
}
|
||||
// 如果旧值不存在,说明赋值成功,返回当前值
|
||||
|
||||
// Dubbo的解决方式,判空后调用依旧无法解决死循环问题
|
||||
// 见:Issue2349Test
|
||||
//value = map.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个Map按照固定大小拆分成多个子Map
|
||||
*
|
||||
|
@ -23,6 +23,7 @@ import cn.hutool.v7.core.util.RuntimeUtil;
|
||||
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -214,7 +215,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
// The data store and its maximum capacity
|
||||
concurrencyLevel = builder.concurrencyLevel;
|
||||
capacity = new AtomicLong(Math.min(builder.capacity, MAXIMUM_CAPACITY));
|
||||
data = new SafeConcurrentHashMap<>(builder.initialCapacity, 0.75f, concurrencyLevel);
|
||||
data = new ConcurrentHashMap<>(builder.initialCapacity, 0.75f, concurrencyLevel);
|
||||
|
||||
// The eviction support
|
||||
weigher = builder.weigher;
|
||||
@ -1139,7 +1140,6 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
* A node contains the key, the weighted value, and the linkage pointers on
|
||||
* the page-replacement algorithm's data structures.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
static final class Node<K, V> extends AtomicReference<WeightedValue<V>>
|
||||
implements Linked<Node<K, V>> {
|
||||
final K key;
|
||||
@ -1382,6 +1382,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
* An entry that allows updates to write through to the map.
|
||||
*/
|
||||
final class WriteThroughEntry extends SimpleEntry<K, V> {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1;
|
||||
|
||||
WriteThroughEntry(final Node<K, V> node) {
|
||||
@ -1394,6 +1395,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
return super.setValue(value);
|
||||
}
|
||||
|
||||
@Serial
|
||||
Object writeReplace() {
|
||||
return new SimpleEntry<>(this);
|
||||
}
|
||||
@ -1402,13 +1404,12 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
/**
|
||||
* A weigher that enforces that the selector falls within a valid range.
|
||||
*/
|
||||
static final class BoundedEntryWeigher<K, V> implements EntryWeigher<K, V>, Serializable {
|
||||
record BoundedEntryWeigher<K, V>(EntryWeigher<? super K, ? super V> weigher) implements EntryWeigher<K, V>, Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1;
|
||||
final EntryWeigher<? super K, ? super V> weigher;
|
||||
|
||||
BoundedEntryWeigher(final EntryWeigher<? super K, ? super V> weigher) {
|
||||
BoundedEntryWeigher {
|
||||
Assert.notNull(weigher);
|
||||
this.weigher = weigher;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1418,6 +1419,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
return weight;
|
||||
}
|
||||
|
||||
@Serial
|
||||
Object writeReplace() {
|
||||
return weigher;
|
||||
}
|
||||
@ -1436,12 +1438,15 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
|
||||
/* ---------------- Serialization Support -------------- */
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1;
|
||||
|
||||
@Serial
|
||||
Object writeReplace() {
|
||||
return new SerializationProxy<>(this);
|
||||
}
|
||||
|
||||
@Serial
|
||||
private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
|
||||
throw new InvalidObjectException("Proxy required");
|
||||
}
|
||||
@ -1468,6 +1473,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
weigher = map.weigher;
|
||||
}
|
||||
|
||||
@Serial
|
||||
Object readResolve() {
|
||||
final ConcurrentLinkedHashMap<K, V> map = new Builder<K, V>()
|
||||
.concurrencyLevel(concurrencyLevel)
|
||||
@ -1479,6 +1485,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
|
||||
return map;
|
||||
}
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 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 cn.hutool.v7.core.map.concurrent;
|
||||
|
||||
import cn.hutool.v7.core.map.MapUtil;
|
||||
import cn.hutool.v7.core.util.JdkUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 安全的ConcurrentHashMap实现<br>
|
||||
* 此类用于解决在JDK8中调用{@link ConcurrentHashMap#computeIfAbsent(Object, Function)}可能造成的死循环问题。<br>
|
||||
* 见:issues#2349<br>
|
||||
* <p>
|
||||
* 相关bug见:https://bugs.openjdk.java.net/browse/JDK-8161372
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
*/
|
||||
public class SafeConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// region == 构造 ==
|
||||
|
||||
/**
|
||||
* 构造,默认初始大小(16)
|
||||
*/
|
||||
public SafeConcurrentHashMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param initialCapacity 预估初始大小
|
||||
*/
|
||||
public SafeConcurrentHashMap(final int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param m 初始键值对
|
||||
*/
|
||||
public SafeConcurrentHashMap(final Map<? extends K, ? extends V> m) {
|
||||
super(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param initialCapacity 初始容量
|
||||
* @param loadFactor 增长系数
|
||||
*/
|
||||
public SafeConcurrentHashMap(final int initialCapacity, final float loadFactor) {
|
||||
super(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param initialCapacity 初始容量
|
||||
* @param loadFactor 增长系数
|
||||
* @param concurrencyLevel 并发级别,即Segment的个数
|
||||
*/
|
||||
public SafeConcurrentHashMap(final int initialCapacity,
|
||||
final float loadFactor, final int concurrencyLevel) {
|
||||
super(initialCapacity, loadFactor, concurrencyLevel);
|
||||
}
|
||||
// endregion == 构造 ==
|
||||
|
||||
@Override
|
||||
public V computeIfAbsent(final K key, final Function<? super K, ? extends V> mappingFunction) {
|
||||
if (JdkUtil.IS_JDK8) {
|
||||
return MapUtil.computeIfAbsentForJdk8(this, key, mappingFunction);
|
||||
} else {
|
||||
return super.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,9 +18,10 @@ package cn.hutool.v7.core.map.reference;
|
||||
|
||||
import cn.hutool.v7.core.lang.ref.Ref;
|
||||
import cn.hutool.v7.core.lang.ref.SoftObj;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
@ -33,13 +34,14 @@ import java.util.concurrent.ConcurrentMap;
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class SoftConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public SoftConcurrentMap() {
|
||||
this(new SafeConcurrentHashMap<>());
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,9 +18,10 @@ package cn.hutool.v7.core.map.reference;
|
||||
|
||||
import cn.hutool.v7.core.lang.ref.Ref;
|
||||
import cn.hutool.v7.core.lang.ref.WeakObj;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
@ -33,13 +34,14 @@ import java.util.concurrent.ConcurrentMap;
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class WeakConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public WeakConcurrentMap() {
|
||||
this(new SafeConcurrentHashMap<>());
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,9 +19,10 @@ package cn.hutool.v7.core.map.reference;
|
||||
import cn.hutool.v7.core.lang.ref.Ref;
|
||||
import cn.hutool.v7.core.lang.ref.StrongObj;
|
||||
import cn.hutool.v7.core.lang.ref.WeakObj;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
@ -34,13 +35,14 @@ import java.util.concurrent.ConcurrentMap;
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class WeakKeyConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public WeakKeyConcurrentMap() {
|
||||
this(new SafeConcurrentHashMap<>());
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 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 cn.hutool.v7.core.reflect.lookup;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
/**
|
||||
* jdk8中如果直接调用{@link MethodHandles#lookup()}获取到的{@link MethodHandles.Lookup}<br>
|
||||
* 在调用findSpecial和unreflectSpecial时会出现权限不够问题,抛出"no private access for invokespecial"异常<br>
|
||||
* 所以通过反射创建MethodHandles.Lookup解决该问题。
|
||||
* <p>
|
||||
* 参考:https://blog.csdn.net/u013202238/article/details/108687086
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class ConstructorLookupFactory implements LookupFactory {
|
||||
|
||||
private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
|
||||
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
|
||||
|
||||
private final Constructor<MethodHandles.Lookup> lookupConstructor;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public ConstructorLookupFactory() {
|
||||
this.lookupConstructor = createLookupConstructor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandles.Lookup lookup(final Class<?> callerClass) {
|
||||
try {
|
||||
return lookupConstructor.newInstance(callerClass, ALLOWED_MODES);
|
||||
} catch (final Exception e) {
|
||||
throw new IllegalStateException("no 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Constructor<MethodHandles.Lookup> createLookupConstructor() {
|
||||
final Constructor<MethodHandles.Lookup> constructor;
|
||||
try {
|
||||
constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
|
||||
} catch (final NoSuchMethodException e) {
|
||||
//可能是jdk8 以下版本
|
||||
throw new IllegalStateException(
|
||||
"There is no 'Lookup(Class, int)' constructor in java.lang.invoke.MethodHandles.", e);
|
||||
}
|
||||
constructor.setAccessible(true);
|
||||
return constructor;
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ import cn.hutool.v7.core.lang.caller.CallerUtil;
|
||||
import cn.hutool.v7.core.reflect.ConstructorUtil;
|
||||
import cn.hutool.v7.core.reflect.ModifierUtil;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.core.util.JdkUtil;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -50,11 +49,7 @@ public class LookupUtil {
|
||||
private static final LookupFactory factory;
|
||||
|
||||
static {
|
||||
if (JdkUtil.IS_JDK8) {
|
||||
factory = new ConstructorLookupFactory();
|
||||
} else {
|
||||
factory = new MethodLookupFactory();
|
||||
}
|
||||
factory = new MethodLookupFactory();
|
||||
}
|
||||
|
||||
// region ----- lookup
|
||||
|
@ -24,9 +24,9 @@ import cn.hutool.v7.core.lang.Console;
|
||||
import cn.hutool.v7.core.lang.mutable.MutableInt;
|
||||
import cn.hutool.v7.core.lang.mutable.MutableObj;
|
||||
import cn.hutool.v7.core.map.MapUtil;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.*;
|
||||
@ -238,7 +238,7 @@ public interface TransformableWrappedStream<T, S extends TransformableWrappedStr
|
||||
default <F> EasyStream<T> distinct(final Function<? super T, F> keyExtractor) {
|
||||
Objects.requireNonNull(keyExtractor);
|
||||
if (isParallel()) {
|
||||
final SafeConcurrentHashMap<F, Boolean> exists = new SafeConcurrentHashMap<>();
|
||||
final ConcurrentHashMap<F, Boolean> exists = new ConcurrentHashMap<>();
|
||||
// 标记是否出现过null值,用于保留第一个出现的null
|
||||
// 由于ConcurrentHashMap的key不能为null,所以用此变量来标记
|
||||
final AtomicBoolean hasNull = new AtomicBoolean(false);
|
||||
@ -321,7 +321,7 @@ public interface TransformableWrappedStream<T, S extends TransformableWrappedStr
|
||||
* @param obj 元素
|
||||
* @return 流
|
||||
*/
|
||||
@SuppressWarnings({"SpellCheckingInspection", "unchecked"})
|
||||
@SuppressWarnings({"unchecked"})
|
||||
default S unshift(final T... obj) {
|
||||
Stream<T> result = unwrap();
|
||||
if (ArrayUtil.isNotEmpty(obj)) {
|
||||
|
@ -17,14 +17,10 @@
|
||||
package cn.hutool.v7.core.text;
|
||||
|
||||
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.text.split.SplitUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -78,9 +74,9 @@ public class AntPathMatcher {
|
||||
|
||||
private volatile Boolean cachePatterns;
|
||||
|
||||
private final Map<String, String[]> tokenizedPatternCache = new SafeConcurrentHashMap<>(256);
|
||||
private final Map<String, String[]> tokenizedPatternCache = new ConcurrentHashMap<>(256);
|
||||
|
||||
private final Map<String, AntPathStringMatcher> stringMatcherCache = new SafeConcurrentHashMap<>(256);
|
||||
private final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<>(256);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -17,7 +17,6 @@
|
||||
package cn.hutool.v7.core.text;
|
||||
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import cn.hutool.v7.core.func.FunctionPool;
|
||||
import cn.hutool.v7.core.text.split.SplitUtil;
|
||||
import cn.hutool.v7.core.util.CharsetUtil;
|
||||
|
||||
@ -102,7 +101,7 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
|
||||
if (null == obj) {
|
||||
return true;
|
||||
} else if (obj instanceof CharSequence) {
|
||||
return 0 == ((CharSequence) obj).length();
|
||||
return ((CharSequence) obj).isEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -231,17 +230,6 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
|
||||
}
|
||||
return charset.decode(data).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* JDK8中,通过{@code String(char[] value, boolean share)}这个内部构造创建String对象。<br>
|
||||
* 此函数通过传入char[],实现zero-copy的String创建,效率很高。但是要求传入的char[]不可以在其他地方修改。
|
||||
*
|
||||
* @param value char[]值,注意这个数组不可修改!!
|
||||
* @return String
|
||||
*/
|
||||
public static String strFast(final char[] value) {
|
||||
return FunctionPool.createString(value);
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
|
@ -29,11 +29,6 @@ public class JdkUtil {
|
||||
* JDK版本
|
||||
*/
|
||||
public static final int JVM_VERSION;
|
||||
/**
|
||||
* 是否JDK8<br>
|
||||
* 由于Hutool基于JDK8编译,当使用JDK版本低于8时,不支持。
|
||||
*/
|
||||
public static final boolean IS_JDK8;
|
||||
/**
|
||||
* 是否大于等于JDK17
|
||||
*/
|
||||
@ -56,7 +51,6 @@ public class JdkUtil {
|
||||
static {
|
||||
// JVM版本
|
||||
JVM_VERSION = _getJvmVersion();
|
||||
IS_JDK8 = 8 == JVM_VERSION;
|
||||
IS_AT_LEAST_JDK17 = JVM_VERSION >= 17;
|
||||
|
||||
// JVM名称
|
||||
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 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 cn.hutool.v7.core.func;
|
||||
|
||||
import cn.hutool.v7.core.collection.ListUtil;
|
||||
import cn.hutool.v7.core.date.DateUtil;
|
||||
import cn.hutool.v7.core.date.StopWatch;
|
||||
import cn.hutool.v7.core.util.RandomUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class FunctionPoolTest {
|
||||
|
||||
@Test
|
||||
public void createStringTest() {
|
||||
// 预热
|
||||
FunctionPool.createString("123".toCharArray());
|
||||
|
||||
// 测试数据
|
||||
final ArrayList<char[]> list = ListUtil.of();
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
list.add(RandomUtil.randomStringLower(100).toCharArray());
|
||||
}
|
||||
|
||||
final StopWatch stopWatch = DateUtil.createStopWatch();
|
||||
stopWatch.start("copy creator");
|
||||
for (final char[] value : list) {
|
||||
new String(value);
|
||||
}
|
||||
stopWatch.stop();
|
||||
|
||||
stopWatch.start("zero copy creator");
|
||||
for (final char[] value : list) {
|
||||
FunctionPool.createString(value);
|
||||
}
|
||||
stopWatch.stop();
|
||||
|
||||
//Console.log(stopWatch.prettyPrint());
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@
|
||||
|
||||
package cn.hutool.v7.core.map;
|
||||
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledForJreRange;
|
||||
@ -25,20 +24,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class Issue2349Test {
|
||||
|
||||
@Test
|
||||
@EnabledForJreRange(max = org.junit.jupiter.api.condition.JRE.JAVA_8)
|
||||
public void computeIfAbsentTest() {
|
||||
// https://blog.csdn.net/xiaochao_bos/article/details/103789991
|
||||
// 使用ConcurrentHashMap会造成死循环
|
||||
// SafeConcurrentHashMap用于修复此问题
|
||||
final ConcurrentHashMap<String, Integer> map = new SafeConcurrentHashMap<>(16);
|
||||
map.computeIfAbsent("AaAa", key -> map.computeIfAbsent("BBBB", key2 -> 42));
|
||||
|
||||
Assertions.assertEquals(2, map.size());
|
||||
Assertions.assertEquals(Integer.valueOf(42), map.get("AaAa"));
|
||||
Assertions.assertEquals(Integer.valueOf(42), map.get("BBBB"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForJreRange(min = org.junit.jupiter.api.condition.JRE.JAVA_9)
|
||||
public void issue11986ForJava17Test() {
|
||||
|
@ -16,18 +16,17 @@
|
||||
|
||||
package cn.hutool.v7.core.map;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import cn.hutool.v7.core.convert.ConvertUtil;
|
||||
import cn.hutool.v7.core.lang.Opt;
|
||||
import cn.hutool.v7.core.reflect.TypeReference;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@ -94,7 +93,7 @@ public class MapUtilTest {
|
||||
// 下单用户,Queue表示正在 .排队. 抢我抢不到的二次元周边!
|
||||
final Queue<String> customers = new ArrayDeque<>(Arrays.asList("刑部尚书手工耿", "木瓜大盗大漠叔", "竹鼠发烧找华农", "朴实无华朱一旦"));
|
||||
// 分组
|
||||
final List<Group> groups = Stream.iterate(0L, i -> ++i).limit(4).map(i -> Group.builder().id(i).build()).collect(Collectors.toList());
|
||||
final List<Group> groups = Stream.iterate(0L, i -> ++i).limit(4).map(i -> Group.builder().id(i).build()).toList();
|
||||
// 如你所见,它是一个map,key由用户id,value由用户组成
|
||||
final Map<Long, User> idUserMap = Stream.iterate(0L, i -> ++i).limit(4).map(i -> User.builder().id(i).name(customers.poll()).build()).collect(Collectors.toMap(User::getId, Function.identity()));
|
||||
// 如你所见,它是一个map,key由分组id,value由用户ids组成,典型的多对多关系
|
||||
@ -259,18 +258,6 @@ public class MapUtilTest {
|
||||
Assertions.assertTrue(v2s.contains("李四"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void computeIfAbsentForJdk8Test() {
|
||||
// https://github.com/apache/dubbo/issues/11986
|
||||
final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
|
||||
// // map.computeIfAbsent("AaAa", key->map.computeIfAbsent("BBBB",key2->42));
|
||||
MapUtil.computeIfAbsentForJdk8(map, "AaAa", key -> map.computeIfAbsent("BBBB", key2 -> 42));
|
||||
|
||||
Assertions.assertEquals(2, map.size());
|
||||
Assertions.assertEquals(Integer.valueOf(42), map.get("AaAa"));
|
||||
Assertions.assertEquals(Integer.valueOf(42), map.get("BBBB"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createMapTest() {
|
||||
final Map<Object, Object> map = MapUtil.createMap(MapUtil.view(new HashMap<>()).getClass());
|
||||
@ -365,7 +352,8 @@ public class MapUtilTest {
|
||||
|
||||
@Test
|
||||
public void issue3162Test() {
|
||||
final Map<String, Object> map = new HashMap<String, Object>() {
|
||||
final Map<String, Object> map = new HashMap<>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
{
|
||||
@ -456,122 +444,6 @@ public class MapUtilTest {
|
||||
assertEquals(3, result.get(1).size());
|
||||
}
|
||||
|
||||
|
||||
// ---------MapUtil.computeIfAbsentForJdk8
|
||||
@Test
|
||||
public void computeIfAbsentForJdk8KeyExistsReturnsExistingValue() {
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
map.put("key", 10);
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(map, "key", k -> 20);
|
||||
assertEquals(10, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computeIfAbsentForJdk8KeyDoesNotExistComputesAndInsertsValue() {
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(map, "key", k -> 20);
|
||||
assertEquals(20, result);
|
||||
assertEquals(20, map.get("key"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computeIfAbsentForJdk8ConcurrentInsertReturnsOldValue() {
|
||||
final ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
|
||||
concurrentMap.put("key", 30);
|
||||
final AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
// 模拟并发插入
|
||||
concurrentMap.computeIfAbsent("key", k -> {
|
||||
counter.incrementAndGet();
|
||||
return 40;
|
||||
});
|
||||
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(concurrentMap, "key", k -> 50);
|
||||
assertEquals(30, result);
|
||||
assertEquals(30, concurrentMap.get("key"));
|
||||
assertEquals(0, counter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computeIfAbsentForJdk8NullValueComputesAndInsertsValue() {
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
map.put("key", null);
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(map, "key", k -> 20);
|
||||
assertEquals(20, result);
|
||||
assertEquals(20, map.get("key"));
|
||||
}
|
||||
|
||||
//--------MapUtil.computeIfAbsent
|
||||
@Test
|
||||
public void computeIfAbsentKeyExistsReturnsExistingValue() {
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
map.put("key", 10);
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(map, "key", k -> 20);
|
||||
assertEquals(10, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computeIfAbsentKeyDoesNotExistComputesAndInsertsValue() {
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(map, "key", k -> 20);
|
||||
assertEquals(20, result);
|
||||
assertEquals(20, map.get("key"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computeIfAbsentConcurrentInsertReturnsOldValue() {
|
||||
final ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
|
||||
concurrentMap.put("key", 30);
|
||||
final AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
// 模拟并发插入
|
||||
concurrentMap.computeIfAbsent("key", k -> {
|
||||
counter.incrementAndGet();
|
||||
return 40;
|
||||
});
|
||||
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(concurrentMap, "key", k -> 50);
|
||||
assertEquals(30, result);
|
||||
assertEquals(30, concurrentMap.get("key"));
|
||||
assertEquals(0, counter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computeIfAbsentNullValueComputesAndInsertsValue() {
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
map.put("key", null);
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(map, "key", k -> 20);
|
||||
assertEquals(20, result);
|
||||
assertEquals(20, map.get("key"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computeIfAbsentEmptyMapInsertsValue() {
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(map, "newKey", k -> 100);
|
||||
assertEquals(100, result);
|
||||
assertEquals(100, map.get("newKey"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computeIfAbsentJdk8KeyExistsReturnsExistingValue() {
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
// 假设JdkUtil.ISJDK8为true
|
||||
map.put("key", 10);
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(map, "key", k -> 20);
|
||||
assertEquals(10, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computeIfAbsentJdk8KeyDoesNotExistComputesAndInsertsValue() {
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
// 假设JdkUtil.ISJDK8为true
|
||||
final Integer result = MapUtil.computeIfAbsentForJdk8(map, "key", k -> 20);
|
||||
assertEquals(20, result);
|
||||
assertEquals(20, map.get("key"));
|
||||
}
|
||||
|
||||
|
||||
//----------valuesOfKeys
|
||||
@Test
|
||||
public void valuesOfKeysEmptyIteratorReturnsEmptyList() {
|
||||
@ -591,6 +463,7 @@ public class MapUtilTest {
|
||||
map.put("b", "2");
|
||||
map.put("c", "3");
|
||||
final Iterator<String> iterator = new ArrayList<String>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -4593258366224032110L;
|
||||
|
||||
{
|
||||
@ -600,6 +473,7 @@ public class MapUtilTest {
|
||||
}.iterator();
|
||||
final ArrayList<String> result = MapUtil.valuesOfKeys(map, iterator);
|
||||
assertEquals(new ArrayList<String>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 7218152799308667271L;
|
||||
|
||||
{
|
||||
@ -616,6 +490,7 @@ public class MapUtilTest {
|
||||
map.put("b", "2");
|
||||
map.put("c", "3");
|
||||
final Iterator<String> iterator = new ArrayList<String>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -5479427021989481058L;
|
||||
|
||||
{
|
||||
@ -625,6 +500,7 @@ public class MapUtilTest {
|
||||
}.iterator();
|
||||
final ArrayList<String> result = MapUtil.valuesOfKeys(map, iterator);
|
||||
assertEquals(new ArrayList<String>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 4390715387901549136L;
|
||||
|
||||
{
|
||||
@ -641,6 +517,7 @@ public class MapUtilTest {
|
||||
map.put("b", "2");
|
||||
map.put("c", "3");
|
||||
final Iterator<String> iterator = new ArrayList<String>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 8510595063492828968L;
|
||||
|
||||
{
|
||||
@ -651,7 +528,9 @@ public class MapUtilTest {
|
||||
}.iterator();
|
||||
final ArrayList<String> result = MapUtil.valuesOfKeys(map, iterator);
|
||||
assertEquals(new ArrayList<String>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 6383576410597048337L;
|
||||
|
||||
{
|
||||
add("1");
|
||||
add(null);
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package cn.hutool.v7.db.dialect;
|
||||
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.db.config.DbConfig;
|
||||
import cn.hutool.v7.db.dialect.impl.*;
|
||||
@ -26,6 +25,7 @@ import cn.hutool.v7.log.LogUtil;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 方言工厂类
|
||||
@ -34,7 +34,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class DialectFactory {
|
||||
|
||||
private static final Map<DataSource, Dialect> DIALECT_POOL = new SafeConcurrentHashMap<>();
|
||||
private static final Map<DataSource, Dialect> DIALECT_POOL = new ConcurrentHashMap<>();
|
||||
|
||||
private DialectFactory() {
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ package cn.hutool.v7.db.ds;
|
||||
import cn.hutool.v7.core.io.IoUtil;
|
||||
import cn.hutool.v7.core.lang.Singleton;
|
||||
import cn.hutool.v7.core.map.MapUtil;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.db.config.ConfigParser;
|
||||
import cn.hutool.v7.db.config.DbConfig;
|
||||
@ -29,6 +28,7 @@ import cn.hutool.v7.log.LogUtil;
|
||||
import java.io.Closeable;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 数据源池,用于支持多数据源。<br>
|
||||
@ -84,7 +84,7 @@ public class DSPool implements Closeable {
|
||||
public DSPool(final ConfigParser configParser, final DSFactory factory) {
|
||||
this.configParser = null != configParser ? configParser : SettingConfigParser.of();
|
||||
this.factory = null != factory ? factory : DSUtil.getDefaultDsFactory();
|
||||
this.pool = new SafeConcurrentHashMap<>();
|
||||
this.pool = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,14 +17,10 @@
|
||||
package cn.hutool.v7.http.html;
|
||||
|
||||
import cn.hutool.v7.core.lang.Console;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.text.CharUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -77,8 +73,8 @@ public final class HtmlFilter {
|
||||
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
|
||||
|
||||
// @xxx could grow large... maybe use sesat's ReferenceMap
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new SafeConcurrentHashMap<>();
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new SafeConcurrentHashMap<>();
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* set of allowed html elements, along with allowed attributes for each element
|
||||
@ -294,7 +290,7 @@ public final class HtmlFilter {
|
||||
|
||||
private String escapeComments(final String s) {
|
||||
final Matcher m = P_COMMENTS.matcher(s);
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
if (m.find()) {
|
||||
final String match = m.group(1); // (.*?)
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
|
||||
@ -334,7 +330,7 @@ public final class HtmlFilter {
|
||||
private String checkTags(String s) {
|
||||
final Matcher m = P_TAGS.matcher(s);
|
||||
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
while (m.find()) {
|
||||
String replaceStr = m.group(1);
|
||||
replaceStr = processTag(replaceStr);
|
||||
@ -477,7 +473,7 @@ public final class HtmlFilter {
|
||||
}
|
||||
|
||||
private String decodeEntities(String s) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
Matcher m = P_ENTITY.matcher(s);
|
||||
while (m.find()) {
|
||||
@ -488,7 +484,7 @@ public final class HtmlFilter {
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
buf = new StringBuffer();
|
||||
buf = new StringBuilder();
|
||||
m = P_ENTITY_UNICODE.matcher(s);
|
||||
while (m.find()) {
|
||||
final String match = m.group(1);
|
||||
@ -498,7 +494,7 @@ public final class HtmlFilter {
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
buf = new StringBuffer();
|
||||
buf = new StringBuilder();
|
||||
m = P_ENCODE.matcher(s);
|
||||
while (m.find()) {
|
||||
final String match = m.group(1);
|
||||
@ -513,7 +509,7 @@ public final class HtmlFilter {
|
||||
}
|
||||
|
||||
private String validateEntities(final String s) {
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
|
||||
// validate entities throughout the string
|
||||
final Matcher m = P_VALID_ENTITIES.matcher(s);
|
||||
@ -529,7 +525,7 @@ public final class HtmlFilter {
|
||||
|
||||
private String encodeQuotes(final String s) {
|
||||
if (encodeQuotes) {
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
final Matcher m = P_VALID_QUOTES.matcher(s);
|
||||
while (m.find()) {
|
||||
final String one = m.group(1); // (>|^)
|
||||
|
@ -18,10 +18,10 @@ package cn.hutool.v7.setting;
|
||||
|
||||
import cn.hutool.v7.core.io.file.FileNameUtil;
|
||||
import cn.hutool.v7.core.io.resource.NoResourceException;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Setting工具类<br>
|
||||
@ -33,7 +33,7 @@ public class SettingUtil {
|
||||
/**
|
||||
* 配置文件缓存
|
||||
*/
|
||||
private static final Map<String, Setting> SETTING_MAP = new SafeConcurrentHashMap<>();
|
||||
private static final Map<String, Setting> SETTING_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 获取当前环境下的配置文件<br>
|
||||
|
@ -17,13 +17,14 @@
|
||||
package cn.hutool.v7.setting.profile;
|
||||
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.setting.Setting;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Profile可以让我们定义一系列的配置信息,然后指定其激活条件。<br>
|
||||
@ -40,6 +41,7 @@ import java.util.Map;
|
||||
*
|
||||
*/
|
||||
public class Profile implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -4189955219454008744L;
|
||||
|
||||
/** 默认环境 */
|
||||
@ -52,7 +54,7 @@ public class Profile implements Serializable {
|
||||
/** 是否使用变量 */
|
||||
private boolean useVar;
|
||||
/** 配置文件缓存 */
|
||||
private final Map<String, Setting> settingMap = new SafeConcurrentHashMap<>();
|
||||
private final Map<String, Setting> settingMap = new ConcurrentHashMap<>();
|
||||
|
||||
// -------------------------------------------------------------------------------- Constructor start
|
||||
/**
|
||||
|
@ -18,10 +18,10 @@ package cn.hutool.v7.setting.props;
|
||||
|
||||
import cn.hutool.v7.core.io.file.FileNameUtil;
|
||||
import cn.hutool.v7.core.io.resource.NoResourceException;
|
||||
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Props工具类<br>
|
||||
@ -35,7 +35,7 @@ public class PropsUtil {
|
||||
/**
|
||||
* 配置文件缓存
|
||||
*/
|
||||
private static final Map<String, Props> propsMap = new SafeConcurrentHashMap<>();
|
||||
private static final Map<String, Props> propsMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 获取当前环境下的配置文件<br>
|
||||
|
Loading…
Reference in New Issue
Block a user