去除JDK8兼容代码

This commit is contained in:
Looly 2025-04-18 12:12:34 +08:00
parent 45419bb8dc
commit 07739d72db
37 changed files with 154 additions and 725 deletions

View File

@ -19,10 +19,10 @@ package cn.hutool.v7.ai;
import cn.hutool.v7.ai.core.AIConfig; import cn.hutool.v7.ai.core.AIConfig;
import cn.hutool.v7.ai.core.AIService; import cn.hutool.v7.ai.core.AIService;
import cn.hutool.v7.ai.core.AIServiceProvider; import cn.hutool.v7.ai.core.AIServiceProvider;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import java.util.Map; import java.util.Map;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* 创建AIModelService的工厂类 * 创建AIModelService的工厂类
@ -32,7 +32,7 @@ import java.util.ServiceLoader;
*/ */
public class AIServiceFactory { public class AIServiceFactory {
private static final Map<String, AIServiceProvider> providers = new SafeConcurrentHashMap<>(); private static final Map<String, AIServiceProvider> providers = new ConcurrentHashMap<>();
// 加载所有 AIModelProvider 实现类 // 加载所有 AIModelProvider 实现类
static { static {

View File

@ -16,10 +16,9 @@
package cn.hutool.v7.ai.core; package cn.hutool.v7.ai.core;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import java.util.Map; import java.util.Map;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* AIConfig实现类的加载器 * AIConfig实现类的加载器
@ -29,7 +28,7 @@ import java.util.ServiceLoader;
*/ */
public class AIConfigRegistry { 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 实现类 // 加载所有 AIConfig 实现类
static { static {

View File

@ -16,9 +16,8 @@
package cn.hutool.v7.ai.core; package cn.hutool.v7.ai.core;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Config基础类定义模型配置的基本属性 * Config基础类定义模型配置的基本属性
@ -35,7 +34,7 @@ public class BaseConfig implements AIConfig {
//具体模型 //具体模型
protected volatile String model; protected volatile String model;
//动态扩展字段 //动态扩展字段
protected Map<String, Object> additionalConfig = new SafeConcurrentHashMap<>(); protected Map<String, Object> additionalConfig = new ConcurrentHashMap<>();
@Override @Override
public void setApiKey(final String apiKey) { public void setApiKey(final String apiKey) {
@ -79,7 +78,7 @@ public class BaseConfig implements AIConfig {
@Override @Override
public Map<String, Object> getAdditionalConfigMap() { public Map<String, Object> getAdditionalConfigMap() {
return new SafeConcurrentHashMap<>(additionalConfig); return new ConcurrentHashMap<>(additionalConfig);
} }
} }

View File

@ -16,7 +16,6 @@
package cn.hutool.v7.core.annotation; 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.reflect.method.MethodUtil;
import cn.hutool.v7.core.text.CharSequenceUtil; import cn.hutool.v7.core.text.CharSequenceUtil;
@ -28,6 +27,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -96,7 +96,7 @@ public final class AnnotationMappingProxy<T extends Annotation> implements Invoc
private AnnotationMappingProxy(final AnnotationMapping<T> annotation) { private AnnotationMappingProxy(final AnnotationMapping<T> annotation) {
final int methodCount = annotation.getAttributes().length; final int methodCount = annotation.getAttributes().length;
this.methods = new HashMap<>(methodCount + 5); this.methods = new HashMap<>(methodCount + 5);
this.valueCache = new SafeConcurrentHashMap<>(methodCount); this.valueCache = new ConcurrentHashMap<>(methodCount);
this.mapping = annotation; this.mapping = annotation;
loadMethods(); loadMethods();
} }

View File

@ -17,31 +17,21 @@
package cn.hutool.v7.core.bean; package cn.hutool.v7.core.bean;
import cn.hutool.v7.core.bean.copier.ValueProvider; 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.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.lang.reflect.Type;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.Map; import java.util.Map;
/** /**
* java.lang.Record 相关工具类封装<br> * java.lang.Record 相关工具类封装
* 来自于FastJSON2的BeanUtils
* *
* @author fastjson2, Looly * @author Looly
* @since 6.0.0 * @since 7.0.0
*/ */
public class RecordUtil { 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类 * 判断给定类是否为Record类
* *
@ -49,27 +39,7 @@ public class RecordUtil {
* @return 是否为Record类 * @return 是否为Record类
*/ */
public static boolean isRecord(final Class<?> clazz) { public static boolean isRecord(final Class<?> clazz) {
if (JdkUtil.JVM_VERSION < 14) { return null != clazz && clazz.isRecord();
// 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;
} }
/** /**
@ -80,31 +50,11 @@ public class RecordUtil {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static Map.Entry<String, Type>[] getRecordComponents(final Class<?> recordClass) { public static Map.Entry<String, Type>[] getRecordComponents(final Class<?> recordClass) {
if (JdkUtil.JVM_VERSION < 14) { final RecordComponent[] components = recordClass.getRecordComponents();
// 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 Map.Entry<String, Type>[] entries = new Map.Entry[components.length]; final Map.Entry<String, Type>[] entries = new Map.Entry[components.length];
for (int i = 0; i < components.length; i++) { for (int i = 0; i < components.length; i++) {
entries[i] = new AbstractMap.SimpleEntry<>( entries[i] = new AbstractMap.SimpleEntry<>(components[i].getName(), components[i].getGenericType());
MethodUtil.invoke(components[i], METHOD_COMPONENT_GET_NAME),
MethodUtil.invoke(components[i], METHOD_COMPONENT_GET_GENERIC_TYPE)
);
} }
return entries; return entries;
} }

View File

@ -21,7 +21,6 @@ import cn.hutool.v7.core.func.SerSupplier;
import cn.hutool.v7.core.lang.Assert; import cn.hutool.v7.core.lang.Assert;
import cn.hutool.v7.core.lang.mutable.Mutable; import cn.hutool.v7.core.lang.mutable.Mutable;
import cn.hutool.v7.core.lang.mutable.MutableObj; 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 cn.hutool.v7.core.map.reference.WeakConcurrentMap;
import java.io.Serializable; import java.io.Serializable;
@ -29,6 +28,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -57,7 +57,7 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
/** /**
* 写的时候每个key一把锁降低锁的粒度 * 写的时候每个key一把锁降低锁的粒度
*/ */
protected final Map<K, Lock> keyLockMap = new SafeConcurrentHashMap<>(); protected final Map<K, Lock> keyLockMap = new ConcurrentHashMap<>();
/** /**
* 构造默认使用{@link WeakHashMap}实现缓存自动清理 * 构造默认使用{@link WeakHashMap}实现缓存自动清理

View File

@ -21,11 +21,12 @@ import cn.hutool.v7.core.cache.CacheListener;
import cn.hutool.v7.core.func.SerSupplier; import cn.hutool.v7.core.func.SerSupplier;
import cn.hutool.v7.core.lang.mutable.Mutable; import cn.hutool.v7.core.lang.mutable.Mutable;
import cn.hutool.v7.core.lang.mutable.MutableObj; 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.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -44,6 +45,7 @@ import java.util.stream.Collectors;
* @author Looly, jodd * @author Looly, jodd
*/ */
public abstract class AbstractCache<K, V> implements Cache<K, V> { public abstract class AbstractCache<K, V> implements Cache<K, V> {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@ -54,7 +56,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
/** /**
* 写的时候每个key一把锁降低锁的粒度 * 写的时候每个key一把锁降低锁的粒度
*/ */
protected final Map<K, Lock> keyLockMap = new SafeConcurrentHashMap<>(); protected final Map<K, Lock> keyLockMap = new ConcurrentHashMap<>();
/** /**
* 返回缓存容量{@code 0}表示无大小限制 * 返回缓存容量{@code 0}表示无大小限制

View File

@ -16,12 +16,12 @@
package cn.hutool.v7.core.collection.set; 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.Collection;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* 通过{@link SafeConcurrentHashMap}实现的线程安全HashSet * 通过{@link ConcurrentHashMap}实现的线程安全HashSet
* *
* @author Looly * @author Looly
* *
@ -29,6 +29,7 @@ import java.util.Collection;
* @since 3.1.0 * @since 3.1.0
*/ */
public class ConcurrentHashSet<E> extends SetFromMap<E> { public class ConcurrentHashSet<E> extends SetFromMap<E> {
@Serial
private static final long serialVersionUID = 7997886765361607470L; private static final long serialVersionUID = 7997886765361607470L;
// ----------------------------------------------------------------------------------- Constructor start // ----------------------------------------------------------------------------------- Constructor start
@ -37,7 +38,7 @@ public class ConcurrentHashSet<E> extends SetFromMap<E> {
* 触发因子为默认的0.75 * 触发因子为默认的0.75
*/ */
public ConcurrentHashSet() { public ConcurrentHashSet() {
super(new SafeConcurrentHashMap<>()); super(new ConcurrentHashMap<>());
} }
/** /**
@ -47,7 +48,7 @@ public class ConcurrentHashSet<E> extends SetFromMap<E> {
* @param initialCapacity 初始大小 * @param initialCapacity 初始大小
*/ */
public ConcurrentHashSet(final int 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 加载因子此参数决定数据增长时触发的百分比 * @param loadFactor 加载因子此参数决定数据增长时触发的百分比
*/ */
public ConcurrentHashSet(final int initialCapacity, final float 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 线程并发度 * @param concurrencyLevel 线程并发度
*/ */
public ConcurrentHashSet(final int initialCapacity, final float loadFactor, final int 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} * @param iter {@link Iterable}
*/ */
public ConcurrentHashSet(final Iterable<E> iter) { 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) { if(iter instanceof Collection) {
this.addAll((Collection<E>)iter); this.addAll((Collection<E>)iter);
}else { }else {

View File

@ -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.Pair;
import cn.hutool.v7.core.lang.tuple.Triple; import cn.hutool.v7.core.lang.tuple.Triple;
import cn.hutool.v7.core.lang.tuple.Tuple; 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.reflect.TypeUtil;
import cn.hutool.v7.core.stream.StreamUtil; import cn.hutool.v7.core.stream.StreamUtil;
import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.datatype.XMLGregorianCalendar;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -38,6 +38,7 @@ import java.nio.file.Path;
import java.time.*; import java.time.*;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerArray; import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicLongArray; import java.util.concurrent.atomic.AtomicLongArray;
@ -55,6 +56,7 @@ import java.util.concurrent.atomic.AtomicReference;
* @since 6.0.0 * @since 6.0.0
*/ */
public class RegisterConverter extends ConverterWithRoot implements Serializable { public class RegisterConverter extends ConverterWithRoot implements Serializable {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@ -169,7 +171,7 @@ public class RegisterConverter extends ConverterWithRoot implements Serializable
if (null == customConverterMap) { if (null == customConverterMap) {
synchronized (this) { synchronized (this) {
if (null == customConverterMap) { if (null == customConverterMap) {
customConverterMap = new SafeConcurrentHashMap<>(); customConverterMap = new ConcurrentHashMap<>();
} }
} }
} }
@ -201,7 +203,7 @@ public class RegisterConverter extends ConverterWithRoot implements Serializable
* @return 默认转换器 * @return 默认转换器
*/ */
private static Map<Class<?>, Converter> initDefault(final Converter rootConverter) { 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); converterMap.put(Character.class, CharacterConverter.INSTANCE);

View File

@ -16,13 +16,13 @@
package cn.hutool.v7.core.data; 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.PatternPool;
import cn.hutool.v7.core.regex.ReUtil; import cn.hutool.v7.core.regex.ReUtil;
import cn.hutool.v7.core.text.StrUtil; import cn.hutool.v7.core.text.StrUtil;
import cn.hutool.v7.core.util.RandomUtil; import cn.hutool.v7.core.util.RandomUtil;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
@ -62,7 +62,7 @@ public class CreditCodeUtil {
private static final Map<Character, Integer> CODE_INDEX_MAP; private static final Map<Character, Integer> CODE_INDEX_MAP;
static { static {
CODE_INDEX_MAP = new SafeConcurrentHashMap<>(); CODE_INDEX_MAP = new ConcurrentHashMap<>();
for (int i = 0; i < BASE_CODE_ARRAY.length; i++) { for (int i = 0; i < BASE_CODE_ARRAY.length; i++) {
CODE_INDEX_MAP.put(BASE_CODE_ARRAY[i], i); CODE_INDEX_MAP.put(BASE_CODE_ARRAY[i], i);
} }

View File

@ -18,11 +18,11 @@ package cn.hutool.v7.core.date.format;
import cn.hutool.v7.core.date.DateUtil; import cn.hutool.v7.core.date.DateUtil;
import cn.hutool.v7.core.lang.Assert; import cn.hutool.v7.core.lang.Assert;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;
/** /**
@ -70,8 +70,8 @@ public class DateFormatManager {
* 构造 * 构造
*/ */
public DateFormatManager() { public DateFormatManager() {
formatterMap = new SafeConcurrentHashMap<>(); formatterMap = new ConcurrentHashMap<>();
parserMap = new SafeConcurrentHashMap<>(); parserMap = new ConcurrentHashMap<>();
// Hutool预设的几种自定义格式 // Hutool预设的几种自定义格式
registerFormatter(FORMAT_SECONDS, (date) -> String.valueOf(Math.floorDiv(date.getTime(), 1000L))); registerFormatter(FORMAT_SECONDS, (date) -> String.valueOf(Math.floorDiv(date.getTime(), 1000L)));

View File

@ -17,11 +17,11 @@
package cn.hutool.v7.core.date.format; package cn.hutool.v7.core.date.format;
import cn.hutool.v7.core.date.DateException; import cn.hutool.v7.core.date.DateException;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import java.io.IOException; import java.io.IOException;
import java.text.DateFormatSymbols; import java.text.DateFormatSymbols;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
/** /**
@ -296,14 +296,11 @@ public class DatePattern {
* @return a new rule with the correct padding * @return a new rule with the correct padding
*/ */
protected static NumberRule selectNumberRule(final int field, final int padding) { protected static NumberRule selectNumberRule(final int field, final int padding) {
switch (padding) { return switch (padding) {
case 1: case 1 -> new UnpaddedNumberField(field);
return new UnpaddedNumberField(field); case 2 -> new TwoDigitNumberField(field);
case 2: default -> new PaddedNumberField(field, padding);
return new TwoDigitNumberField(field); };
default:
return new PaddedNumberField(field, padding);
}
} }
// Rules // 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. * @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) { static Iso8601_Rule getRule(final int tokenLen) {
switch (tokenLen) { return switch (tokenLen) {
case 1: case 1 -> Iso8601_Rule.ISO8601_HOURS;
return Iso8601_Rule.ISO8601_HOURS; case 2 -> Iso8601_Rule.ISO8601_HOURS_MINUTES;
case 2: case 3 -> Iso8601_Rule.ISO8601_HOURS_COLON_MINUTES;
return Iso8601_Rule.ISO8601_HOURS_MINUTES; default -> throw new IllegalArgumentException("invalid number of X");
case 3: };
return Iso8601_Rule.ISO8601_HOURS_COLON_MINUTES;
default:
throw new IllegalArgumentException("invalid number of X");
}
} }
final int length; 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> * <p>
@ -1110,8 +1103,7 @@ public class DatePattern {
if (this == obj) { if (this == obj) {
return true; return true;
} }
if (obj instanceof TimeZoneDisplayKey) { if (obj instanceof TimeZoneDisplayKey other) {
final TimeZoneDisplayKey other = (TimeZoneDisplayKey) obj;
return mTimeZone.equals(other.mTimeZone) && mStyle == other.mStyle && mLocale.equals(other.mLocale); return mTimeZone.equals(other.mTimeZone) && mStyle == other.mStyle && mLocale.equals(other.mLocale);
} }
return false; return false;

View File

@ -18,13 +18,13 @@ package cn.hutool.v7.core.date.format;
import cn.hutool.v7.core.lang.Assert; import cn.hutool.v7.core.lang.Assert;
import cn.hutool.v7.core.lang.tuple.Tuple; import cn.hutool.v7.core.lang.tuple.Tuple;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.Format; import java.text.Format;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
/** /**
@ -40,9 +40,9 @@ abstract class FormatCache<F extends Format> {
*/ */
static final int NONE = -1; 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);
/** /**
* 使用默认的patterntimezone和locale获得缓存中的实例 * 使用默认的patterntimezone和locale获得缓存中的实例

View File

@ -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.FastDateFormat;
import cn.hutool.v7.core.date.format.FastDatePrinter; import cn.hutool.v7.core.date.format.FastDatePrinter;
import cn.hutool.v7.core.date.format.SimpleDateBasic; import cn.hutool.v7.core.date.format.SimpleDateBasic;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import cn.hutool.v7.core.text.StrUtil; import cn.hutool.v7.core.text.StrUtil;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.Serial;
import java.text.DateFormatSymbols; import java.text.DateFormatSymbols;
import java.text.ParsePosition; import java.text.ParsePosition;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -40,6 +41,7 @@ import java.util.regex.Pattern;
* @since 2.16.2 * @since 2.16.2
*/ */
public class FastDateParser extends SimpleDateBasic implements PositionDateParser { public class FastDateParser extends SimpleDateBasic implements PositionDateParser {
@Serial
private static final long serialVersionUID = -3199383897950947498L; private static final long serialVersionUID = -3199383897950947498L;
static final Locale JAPANESE_IMPERIAL = new Locale("ja", "JP", "JP"); 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 IOException if there is an IO issue.
* @throws ClassNotFoundException if a class cannot be found. * @throws ClassNotFoundException if a class cannot be found.
*/ */
@Serial
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); 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) { private Strategy getStrategy(final char f, final int width, final Calendar definingCalendar) {
switch (f) { switch (f) {
default:
throw new IllegalArgumentException("Format '" + f + "' not supported");
case 'D': case 'D':
return DAY_OF_YEAR_STRATEGY; return DAY_OF_YEAR_STRATEGY;
case 'E': case 'E':
@ -428,6 +429,8 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
//$FALL-THROUGH$ //$FALL-THROUGH$
case 'z': case 'z':
return getLocaleSpecificStrategy(Calendar.ZONE_OFFSET, definingCalendar); 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) { private static ConcurrentMap<Locale, Strategy> getCache(final int field) {
synchronized (CACHES) { synchronized (CACHES) {
if (CACHES[field] == null) { if (CACHES[field] == null) {
CACHES[field] = new SafeConcurrentHashMap<>(3); CACHES[field] = new ConcurrentHashMap<>(3);
} }
return CACHES[field]; return CACHES[field];
} }
@ -669,15 +672,14 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
final TzInfo standard = new TzInfo(tz, false); final TzInfo standard = new TzInfo(tz, false);
TzInfo tzInfo = standard; TzInfo tzInfo = standard;
for (int i = 1; i < zoneNames.length; ++i) { for (int i = 1; i < zoneNames.length; ++i) {
switch (i) { tzInfo = switch (i) {
case 3: // offset 3 is long daylight savings (or summertime) name case 3 -> // offset 3 is long daylight savings (or summertime) name
// offset 4 is the short summertime name // offset 4 is the short summertime name
tzInfo = new TzInfo(tz, true); new TzInfo(tz, true);
break; case 5 -> // offset 5 starts additional names, probably standard time
case 5: // offset 5 starts additional names, probably standard time standard;
tzInfo = standard; default -> tzInfo;
break; };
}
if (zoneNames[i] != null) { if (zoneNames[i] != null) {
final String key = zoneNames[i].toLowerCase(locale); final String key = zoneNames[i].toLowerCase(locale);
// ignore the data associated with duplicates supplied in // 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. * @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) { static Strategy getStrategy(final int tokenLen) {
switch (tokenLen) { return switch (tokenLen) {
case 1: case 1 -> ISO_8601_1_STRATEGY;
return ISO_8601_1_STRATEGY; case 2 -> ISO_8601_2_STRATEGY;
case 2: case 3 -> ISO_8601_3_STRATEGY;
return ISO_8601_2_STRATEGY; default -> throw new IllegalArgumentException("invalid number of X");
case 3: };
return ISO_8601_3_STRATEGY;
default:
throw new IllegalArgumentException("invalid number of X");
}
} }
} }

View File

@ -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);
}
}
}

View File

@ -18,11 +18,11 @@ package cn.hutool.v7.core.lang;
import cn.hutool.v7.core.array.ArrayUtil; import cn.hutool.v7.core.array.ArrayUtil;
import cn.hutool.v7.core.func.SerSupplier; 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.reflect.ConstructorUtil;
import cn.hutool.v7.core.text.StrUtil; import cn.hutool.v7.core.text.StrUtil;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -33,7 +33,7 @@ import java.util.stream.Collectors;
*/ */
public final class Singleton { public final class Singleton {
private static final SafeConcurrentHashMap<String, Object> POOL = new SafeConcurrentHashMap<>(); private static final ConcurrentHashMap<String, Object> POOL = new ConcurrentHashMap<>();
private Singleton() { private Singleton() {
} }

View File

@ -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.collection.iter.IterUtil;
import cn.hutool.v7.core.func.SerConsumer3; import cn.hutool.v7.core.func.SerConsumer3;
import cn.hutool.v7.core.lang.Assert; 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.reflect.ConstructorUtil;
import cn.hutool.v7.core.text.StrUtil; import cn.hutool.v7.core.text.StrUtil;
import cn.hutool.v7.core.util.ObjUtil; import cn.hutool.v7.core.util.ObjUtil;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.*; import java.util.function.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -196,45 +194,6 @@ public class MapUtil extends MapGetUtil {
return new IdentityHashMap<>(size); 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<br>
* 传入抽象Map{@link AbstractMap}{@link Map}类将默认创建{@link HashMap} * 传入抽象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)); final HashMap<Object, Object> map = new HashMap<>((int) (array.length * 1.5));
for (int i = 0; i < array.length; i++) { for (int i = 0; i < array.length; i++) {
final Object object = array[i]; final Object object = array[i];
if (object instanceof Map.Entry) { if (object instanceof Map.Entry entry) {
final Map.Entry entry = (Map.Entry) object;
map.put(entry.getKey(), entry.getValue()); map.put(entry.getKey(), entry.getValue());
} else if (object instanceof Object[]) { } else if (object instanceof Object[] entry) {
final Object[] entry = (Object[]) object;
if (entry.length > 1) { if (entry.length > 1) {
map.put(entry[0], entry[1]); map.put(entry[0], entry[1]);
} }
@ -447,8 +404,7 @@ public class MapUtil extends MapGetUtil {
map.put(key, value); map.put(key, value);
} }
} }
} else if (object instanceof Iterator) { } else if (object instanceof Iterator iter) {
final Iterator iter = ((Iterator) object);
if (iter.hasNext()) { if (iter.hasNext()) {
final Object key = iter.next(); final Object key = iter.next();
if (iter.hasNext()) { if (iter.hasNext()) {
@ -882,9 +838,8 @@ public class MapUtil extends MapGetUtil {
return null; return null;
} }
if (map instanceof TreeMap) { if (map instanceof TreeMap<K, V> result) {
// 已经是可排序Map此时只有比较器一致才返回原map // 已经是可排序Map此时只有比较器一致才返回原map
final TreeMap<K, V> result = (TreeMap<K, V>) map;
if (null == comparator || comparator.equals(result.comparator())) { if (null == comparator || comparator.equals(result.comparator())) {
return result; return result;
} }
@ -1358,43 +1313,6 @@ public class MapUtil extends MapGetUtil {
return map; 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 * 将一个Map按照固定大小拆分成多个子Map
* *

View File

@ -23,6 +23,7 @@ import cn.hutool.v7.core.util.RuntimeUtil;
import java.io.InvalidObjectException; import java.io.InvalidObjectException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; 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 // The data store and its maximum capacity
concurrencyLevel = builder.concurrencyLevel; concurrencyLevel = builder.concurrencyLevel;
capacity = new AtomicLong(Math.min(builder.capacity, MAXIMUM_CAPACITY)); 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 // The eviction support
weigher = builder.weigher; 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 * A node contains the key, the weighted value, and the linkage pointers on
* the page-replacement algorithm's data structures. * the page-replacement algorithm's data structures.
*/ */
@SuppressWarnings("serial")
static final class Node<K, V> extends AtomicReference<WeightedValue<V>> static final class Node<K, V> extends AtomicReference<WeightedValue<V>>
implements Linked<Node<K, V>> { implements Linked<Node<K, V>> {
final K key; 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. * An entry that allows updates to write through to the map.
*/ */
final class WriteThroughEntry extends SimpleEntry<K, V> { final class WriteThroughEntry extends SimpleEntry<K, V> {
@Serial
private static final long serialVersionUID = 1; private static final long serialVersionUID = 1;
WriteThroughEntry(final Node<K, V> node) { WriteThroughEntry(final Node<K, V> node) {
@ -1394,6 +1395,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
return super.setValue(value); return super.setValue(value);
} }
@Serial
Object writeReplace() { Object writeReplace() {
return new SimpleEntry<>(this); 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. * 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; 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); Assert.notNull(weigher);
this.weigher = weigher;
} }
@Override @Override
@ -1418,6 +1419,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
return weight; return weight;
} }
@Serial
Object writeReplace() { Object writeReplace() {
return weigher; return weigher;
} }
@ -1436,12 +1438,15 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
/* ---------------- Serialization Support -------------- */ /* ---------------- Serialization Support -------------- */
@Serial
private static final long serialVersionUID = 1; private static final long serialVersionUID = 1;
@Serial
Object writeReplace() { Object writeReplace() {
return new SerializationProxy<>(this); return new SerializationProxy<>(this);
} }
@Serial
private void readObject(final ObjectInputStream stream) throws InvalidObjectException { private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required"); throw new InvalidObjectException("Proxy required");
} }
@ -1468,6 +1473,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
weigher = map.weigher; weigher = map.weigher;
} }
@Serial
Object readResolve() { Object readResolve() {
final ConcurrentLinkedHashMap<K, V> map = new Builder<K, V>() final ConcurrentLinkedHashMap<K, V> map = new Builder<K, V>()
.concurrencyLevel(concurrencyLevel) .concurrencyLevel(concurrencyLevel)
@ -1479,6 +1485,7 @@ public final class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V>
return map; return map;
} }
@Serial
private static final long serialVersionUID = 1; private static final long serialVersionUID = 1;
} }

View File

@ -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);
}
}
}

View File

@ -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.Ref;
import cn.hutool.v7.core.lang.ref.SoftObj; 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.lang.ref.ReferenceQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
/** /**
@ -33,13 +34,14 @@ import java.util.concurrent.ConcurrentMap;
* @since 6.0.0 * @since 6.0.0
*/ */
public class SoftConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> { public class SoftConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 构造 * 构造
*/ */
public SoftConcurrentMap() { public SoftConcurrentMap() {
this(new SafeConcurrentHashMap<>()); this(new ConcurrentHashMap<>());
} }
/** /**

View File

@ -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.Ref;
import cn.hutool.v7.core.lang.ref.WeakObj; 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.lang.ref.ReferenceQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
/** /**
@ -33,13 +34,14 @@ import java.util.concurrent.ConcurrentMap;
* @since 6.0.0 * @since 6.0.0
*/ */
public class WeakConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> { public class WeakConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 构造 * 构造
*/ */
public WeakConcurrentMap() { public WeakConcurrentMap() {
this(new SafeConcurrentHashMap<>()); this(new ConcurrentHashMap<>());
} }
/** /**

View File

@ -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.Ref;
import cn.hutool.v7.core.lang.ref.StrongObj; import cn.hutool.v7.core.lang.ref.StrongObj;
import cn.hutool.v7.core.lang.ref.WeakObj; 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.lang.ref.ReferenceQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
/** /**
@ -34,13 +35,14 @@ import java.util.concurrent.ConcurrentMap;
* @since 6.0.0 * @since 6.0.0
*/ */
public class WeakKeyConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> { public class WeakKeyConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 构造 * 构造
*/ */
public WeakKeyConcurrentMap() { public WeakKeyConcurrentMap() {
this(new SafeConcurrentHashMap<>()); this(new ConcurrentHashMap<>());
} }
/** /**

View File

@ -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;
}
}

View File

@ -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.ConstructorUtil;
import cn.hutool.v7.core.reflect.ModifierUtil; import cn.hutool.v7.core.reflect.ModifierUtil;
import cn.hutool.v7.core.text.StrUtil; import cn.hutool.v7.core.text.StrUtil;
import cn.hutool.v7.core.util.JdkUtil;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -50,11 +49,7 @@ public class LookupUtil {
private static final LookupFactory factory; private static final LookupFactory factory;
static { static {
if (JdkUtil.IS_JDK8) { factory = new MethodLookupFactory();
factory = new ConstructorLookupFactory();
} else {
factory = new MethodLookupFactory();
}
} }
// region ----- lookup // region ----- lookup

View File

@ -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.MutableInt;
import cn.hutool.v7.core.lang.mutable.MutableObj; import cn.hutool.v7.core.lang.mutable.MutableObj;
import cn.hutool.v7.core.map.MapUtil; import cn.hutool.v7.core.map.MapUtil;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.*; 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) { default <F> EasyStream<T> distinct(final Function<? super T, F> keyExtractor) {
Objects.requireNonNull(keyExtractor); Objects.requireNonNull(keyExtractor);
if (isParallel()) { if (isParallel()) {
final SafeConcurrentHashMap<F, Boolean> exists = new SafeConcurrentHashMap<>(); final ConcurrentHashMap<F, Boolean> exists = new ConcurrentHashMap<>();
// 标记是否出现过null值用于保留第一个出现的null // 标记是否出现过null值用于保留第一个出现的null
// 由于ConcurrentHashMap的key不能为null所以用此变量来标记 // 由于ConcurrentHashMap的key不能为null所以用此变量来标记
final AtomicBoolean hasNull = new AtomicBoolean(false); final AtomicBoolean hasNull = new AtomicBoolean(false);
@ -321,7 +321,7 @@ public interface TransformableWrappedStream<T, S extends TransformableWrappedStr
* @param obj 元素 * @param obj 元素
* @return * @return
*/ */
@SuppressWarnings({"SpellCheckingInspection", "unchecked"}) @SuppressWarnings({"unchecked"})
default S unshift(final T... obj) { default S unshift(final T... obj) {
Stream<T> result = unwrap(); Stream<T> result = unwrap();
if (ArrayUtil.isNotEmpty(obj)) { if (ArrayUtil.isNotEmpty(obj)) {

View File

@ -17,14 +17,10 @@
package cn.hutool.v7.core.text; package cn.hutool.v7.core.text;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import cn.hutool.v7.core.text.split.SplitUtil; import cn.hutool.v7.core.text.split.SplitUtil;
import java.util.ArrayList; import java.util.*;
import java.util.Comparator; import java.util.concurrent.ConcurrentHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -78,9 +74,9 @@ public class AntPathMatcher {
private volatile Boolean cachePatterns; 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);
/** /**

View File

@ -17,7 +17,6 @@
package cn.hutool.v7.core.text; package cn.hutool.v7.core.text;
import cn.hutool.v7.core.array.ArrayUtil; 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.text.split.SplitUtil;
import cn.hutool.v7.core.util.CharsetUtil; import cn.hutool.v7.core.util.CharsetUtil;
@ -102,7 +101,7 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
if (null == obj) { if (null == obj) {
return true; return true;
} else if (obj instanceof CharSequence) { } else if (obj instanceof CharSequence) {
return 0 == ((CharSequence) obj).length(); return ((CharSequence) obj).isEmpty();
} }
return false; return false;
} }
@ -231,17 +230,6 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
} }
return charset.decode(data).toString(); 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 // endregion
/** /**

View File

@ -29,11 +29,6 @@ public class JdkUtil {
* JDK版本 * JDK版本
*/ */
public static final int JVM_VERSION; public static final int JVM_VERSION;
/**
* 是否JDK8<br>
* 由于Hutool基于JDK8编译当使用JDK版本低于8时不支持
*/
public static final boolean IS_JDK8;
/** /**
* 是否大于等于JDK17 * 是否大于等于JDK17
*/ */
@ -56,7 +51,6 @@ public class JdkUtil {
static { static {
// JVM版本 // JVM版本
JVM_VERSION = _getJvmVersion(); JVM_VERSION = _getJvmVersion();
IS_JDK8 = 8 == JVM_VERSION;
IS_AT_LEAST_JDK17 = JVM_VERSION >= 17; IS_AT_LEAST_JDK17 = JVM_VERSION >= 17;
// JVM名称 // JVM名称

View File

@ -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());
}
}

View File

@ -16,7 +16,6 @@
package cn.hutool.v7.core.map; 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.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.EnabledForJreRange;
@ -25,20 +24,6 @@ import java.util.concurrent.ConcurrentHashMap;
public class Issue2349Test { 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 @Test
@EnabledForJreRange(min = org.junit.jupiter.api.condition.JRE.JAVA_9) @EnabledForJreRange(min = org.junit.jupiter.api.condition.JRE.JAVA_9)
public void issue11986ForJava17Test() { public void issue11986ForJava17Test() {

View File

@ -16,18 +16,17 @@
package cn.hutool.v7.core.map; package cn.hutool.v7.core.map;
import lombok.Builder;
import lombok.Data;
import cn.hutool.v7.core.convert.ConvertUtil; import cn.hutool.v7.core.convert.ConvertUtil;
import cn.hutool.v7.core.lang.Opt; import cn.hutool.v7.core.lang.Opt;
import cn.hutool.v7.core.reflect.TypeReference; import cn.hutool.v7.core.reflect.TypeReference;
import cn.hutool.v7.core.text.StrUtil; import cn.hutool.v7.core.text.StrUtil;
import lombok.Builder;
import lombok.Data;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.Serial;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -94,7 +93,7 @@ public class MapUtilTest {
// 下单用户Queue表示正在 .排队. 抢我抢不到的二次元周边 // 下单用户Queue表示正在 .排队. 抢我抢不到的二次元周边
final Queue<String> customers = new ArrayDeque<>(Arrays.asList("刑部尚书手工耿", "木瓜大盗大漠叔", "竹鼠发烧找华农", "朴实无华朱一旦")); 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();
// 如你所见它是一个mapkey由用户idvalue由用户组成 // 如你所见它是一个mapkey由用户idvalue由用户组成
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())); 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()));
// 如你所见它是一个mapkey由分组idvalue由用户ids组成典型的多对多关系 // 如你所见它是一个mapkey由分组idvalue由用户ids组成典型的多对多关系
@ -259,18 +258,6 @@ public class MapUtilTest {
Assertions.assertTrue(v2s.contains("李四")); 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 @Test
void createMapTest() { void createMapTest() {
final Map<Object, Object> map = MapUtil.createMap(MapUtil.view(new HashMap<>()).getClass()); final Map<Object, Object> map = MapUtil.createMap(MapUtil.view(new HashMap<>()).getClass());
@ -365,7 +352,8 @@ public class MapUtilTest {
@Test @Test
public void issue3162Test() { 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; private static final long serialVersionUID = 1L;
{ {
@ -456,122 +444,6 @@ public class MapUtilTest {
assertEquals(3, result.get(1).size()); 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 //----------valuesOfKeys
@Test @Test
public void valuesOfKeysEmptyIteratorReturnsEmptyList() { public void valuesOfKeysEmptyIteratorReturnsEmptyList() {
@ -591,6 +463,7 @@ public class MapUtilTest {
map.put("b", "2"); map.put("b", "2");
map.put("c", "3"); map.put("c", "3");
final Iterator<String> iterator = new ArrayList<String>() { final Iterator<String> iterator = new ArrayList<String>() {
@Serial
private static final long serialVersionUID = -4593258366224032110L; private static final long serialVersionUID = -4593258366224032110L;
{ {
@ -600,6 +473,7 @@ public class MapUtilTest {
}.iterator(); }.iterator();
final ArrayList<String> result = MapUtil.valuesOfKeys(map, iterator); final ArrayList<String> result = MapUtil.valuesOfKeys(map, iterator);
assertEquals(new ArrayList<String>() { assertEquals(new ArrayList<String>() {
@Serial
private static final long serialVersionUID = 7218152799308667271L; private static final long serialVersionUID = 7218152799308667271L;
{ {
@ -616,6 +490,7 @@ public class MapUtilTest {
map.put("b", "2"); map.put("b", "2");
map.put("c", "3"); map.put("c", "3");
final Iterator<String> iterator = new ArrayList<String>() { final Iterator<String> iterator = new ArrayList<String>() {
@Serial
private static final long serialVersionUID = -5479427021989481058L; private static final long serialVersionUID = -5479427021989481058L;
{ {
@ -625,6 +500,7 @@ public class MapUtilTest {
}.iterator(); }.iterator();
final ArrayList<String> result = MapUtil.valuesOfKeys(map, iterator); final ArrayList<String> result = MapUtil.valuesOfKeys(map, iterator);
assertEquals(new ArrayList<String>() { assertEquals(new ArrayList<String>() {
@Serial
private static final long serialVersionUID = 4390715387901549136L; private static final long serialVersionUID = 4390715387901549136L;
{ {
@ -641,6 +517,7 @@ public class MapUtilTest {
map.put("b", "2"); map.put("b", "2");
map.put("c", "3"); map.put("c", "3");
final Iterator<String> iterator = new ArrayList<String>() { final Iterator<String> iterator = new ArrayList<String>() {
@Serial
private static final long serialVersionUID = 8510595063492828968L; private static final long serialVersionUID = 8510595063492828968L;
{ {
@ -651,7 +528,9 @@ public class MapUtilTest {
}.iterator(); }.iterator();
final ArrayList<String> result = MapUtil.valuesOfKeys(map, iterator); final ArrayList<String> result = MapUtil.valuesOfKeys(map, iterator);
assertEquals(new ArrayList<String>() { assertEquals(new ArrayList<String>() {
@Serial
private static final long serialVersionUID = 6383576410597048337L; private static final long serialVersionUID = 6383576410597048337L;
{ {
add("1"); add("1");
add(null); add(null);

View File

@ -16,7 +16,6 @@
package cn.hutool.v7.db.dialect; 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.core.text.StrUtil;
import cn.hutool.v7.db.config.DbConfig; import cn.hutool.v7.db.config.DbConfig;
import cn.hutool.v7.db.dialect.impl.*; import cn.hutool.v7.db.dialect.impl.*;
@ -26,6 +25,7 @@ import cn.hutool.v7.log.LogUtil;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* 方言工厂类 * 方言工厂类
@ -34,7 +34,7 @@ import java.util.Map;
*/ */
public class DialectFactory { 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() { private DialectFactory() {
} }

View File

@ -19,7 +19,6 @@ package cn.hutool.v7.db.ds;
import cn.hutool.v7.core.io.IoUtil; import cn.hutool.v7.core.io.IoUtil;
import cn.hutool.v7.core.lang.Singleton; import cn.hutool.v7.core.lang.Singleton;
import cn.hutool.v7.core.map.MapUtil; 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.core.text.StrUtil;
import cn.hutool.v7.db.config.ConfigParser; import cn.hutool.v7.db.config.ConfigParser;
import cn.hutool.v7.db.config.DbConfig; import cn.hutool.v7.db.config.DbConfig;
@ -29,6 +28,7 @@ import cn.hutool.v7.log.LogUtil;
import java.io.Closeable; import java.io.Closeable;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* 数据源池用于支持多数据源<br> * 数据源池用于支持多数据源<br>
@ -84,7 +84,7 @@ public class DSPool implements Closeable {
public DSPool(final ConfigParser configParser, final DSFactory factory) { public DSPool(final ConfigParser configParser, final DSFactory factory) {
this.configParser = null != configParser ? configParser : SettingConfigParser.of(); this.configParser = null != configParser ? configParser : SettingConfigParser.of();
this.factory = null != factory ? factory : DSUtil.getDefaultDsFactory(); this.factory = null != factory ? factory : DSUtil.getDefaultDsFactory();
this.pool = new SafeConcurrentHashMap<>(); this.pool = new ConcurrentHashMap<>();
} }
/** /**

View File

@ -17,14 +17,10 @@
package cn.hutool.v7.http.html; package cn.hutool.v7.http.html;
import cn.hutool.v7.core.lang.Console; import cn.hutool.v7.core.lang.Console;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import cn.hutool.v7.core.text.CharUtil; import cn.hutool.v7.core.text.CharUtil;
import java.util.ArrayList; import java.util.*;
import java.util.Collections; import java.util.concurrent.ConcurrentHashMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -77,8 +73,8 @@ public final class HtmlFilter {
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
// @xxx could grow large... maybe use sesat's ReferenceMap // @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_PAIR_BLANKS = new ConcurrentHashMap<>();
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new SafeConcurrentHashMap<>(); private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
/** /**
* set of allowed html elements, along with allowed attributes for each element * 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) { private String escapeComments(final String s) {
final Matcher m = P_COMMENTS.matcher(s); final Matcher m = P_COMMENTS.matcher(s);
final StringBuffer buf = new StringBuffer(); final StringBuilder buf = new StringBuilder();
if (m.find()) { if (m.find()) {
final String match = m.group(1); // (.*?) final String match = m.group(1); // (.*?)
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->")); m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
@ -334,7 +330,7 @@ public final class HtmlFilter {
private String checkTags(String s) { private String checkTags(String s) {
final Matcher m = P_TAGS.matcher(s); final Matcher m = P_TAGS.matcher(s);
final StringBuffer buf = new StringBuffer(); final StringBuilder buf = new StringBuilder();
while (m.find()) { while (m.find()) {
String replaceStr = m.group(1); String replaceStr = m.group(1);
replaceStr = processTag(replaceStr); replaceStr = processTag(replaceStr);
@ -477,7 +473,7 @@ public final class HtmlFilter {
} }
private String decodeEntities(String s) { private String decodeEntities(String s) {
StringBuffer buf = new StringBuffer(); StringBuilder buf = new StringBuilder();
Matcher m = P_ENTITY.matcher(s); Matcher m = P_ENTITY.matcher(s);
while (m.find()) { while (m.find()) {
@ -488,7 +484,7 @@ public final class HtmlFilter {
m.appendTail(buf); m.appendTail(buf);
s = buf.toString(); s = buf.toString();
buf = new StringBuffer(); buf = new StringBuilder();
m = P_ENTITY_UNICODE.matcher(s); m = P_ENTITY_UNICODE.matcher(s);
while (m.find()) { while (m.find()) {
final String match = m.group(1); final String match = m.group(1);
@ -498,7 +494,7 @@ public final class HtmlFilter {
m.appendTail(buf); m.appendTail(buf);
s = buf.toString(); s = buf.toString();
buf = new StringBuffer(); buf = new StringBuilder();
m = P_ENCODE.matcher(s); m = P_ENCODE.matcher(s);
while (m.find()) { while (m.find()) {
final String match = m.group(1); final String match = m.group(1);
@ -513,7 +509,7 @@ public final class HtmlFilter {
} }
private String validateEntities(final String s) { private String validateEntities(final String s) {
final StringBuffer buf = new StringBuffer(); final StringBuilder buf = new StringBuilder();
// validate entities throughout the string // validate entities throughout the string
final Matcher m = P_VALID_ENTITIES.matcher(s); final Matcher m = P_VALID_ENTITIES.matcher(s);
@ -529,7 +525,7 @@ public final class HtmlFilter {
private String encodeQuotes(final String s) { private String encodeQuotes(final String s) {
if (encodeQuotes) { if (encodeQuotes) {
final StringBuffer buf = new StringBuffer(); final StringBuilder buf = new StringBuilder();
final Matcher m = P_VALID_QUOTES.matcher(s); final Matcher m = P_VALID_QUOTES.matcher(s);
while (m.find()) { while (m.find()) {
final String one = m.group(1); // (>|^) final String one = m.group(1); // (>|^)

View File

@ -18,10 +18,10 @@ package cn.hutool.v7.setting;
import cn.hutool.v7.core.io.file.FileNameUtil; import cn.hutool.v7.core.io.file.FileNameUtil;
import cn.hutool.v7.core.io.resource.NoResourceException; import cn.hutool.v7.core.io.resource.NoResourceException;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import cn.hutool.v7.core.text.StrUtil; import cn.hutool.v7.core.text.StrUtil;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Setting工具类<br> * 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> * 获取当前环境下的配置文件<br>

View File

@ -17,13 +17,14 @@
package cn.hutool.v7.setting.profile; package cn.hutool.v7.setting.profile;
import cn.hutool.v7.core.lang.Assert; 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.core.text.StrUtil;
import cn.hutool.v7.setting.Setting; import cn.hutool.v7.setting.Setting;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Profile可以让我们定义一系列的配置信息然后指定其激活条件<br> * Profile可以让我们定义一系列的配置信息然后指定其激活条件<br>
@ -40,6 +41,7 @@ import java.util.Map;
* *
*/ */
public class Profile implements Serializable { public class Profile implements Serializable {
@Serial
private static final long serialVersionUID = -4189955219454008744L; private static final long serialVersionUID = -4189955219454008744L;
/** 默认环境 */ /** 默认环境 */
@ -52,7 +54,7 @@ public class Profile implements Serializable {
/** 是否使用变量 */ /** 是否使用变量 */
private boolean useVar; private boolean useVar;
/** 配置文件缓存 */ /** 配置文件缓存 */
private final Map<String, Setting> settingMap = new SafeConcurrentHashMap<>(); private final Map<String, Setting> settingMap = new ConcurrentHashMap<>();
// -------------------------------------------------------------------------------- Constructor start // -------------------------------------------------------------------------------- Constructor start
/** /**

View File

@ -18,10 +18,10 @@ package cn.hutool.v7.setting.props;
import cn.hutool.v7.core.io.file.FileNameUtil; import cn.hutool.v7.core.io.file.FileNameUtil;
import cn.hutool.v7.core.io.resource.NoResourceException; import cn.hutool.v7.core.io.resource.NoResourceException;
import cn.hutool.v7.core.map.concurrent.SafeConcurrentHashMap;
import cn.hutool.v7.core.text.StrUtil; import cn.hutool.v7.core.text.StrUtil;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Props工具类<br> * 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> * 获取当前环境下的配置文件<br>