去除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.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 {

View File

@ -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 {

View File

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

View File

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

View File

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

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.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}实现缓存自动清理

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.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}表示无大小限制

View File

@ -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 {

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.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);

View File

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

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.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)));

View File

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

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.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);
/**
* 使用默认的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.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");
};
}
}

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.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() {
}

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.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
*

View File

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

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.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<>());
}
/**

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.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<>());
}
/**

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.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<>());
}
/**

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.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

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.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)) {

View File

@ -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);
/**

View File

@ -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
/**

View File

@ -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名称

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;
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() {

View File

@ -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();
// 如你所见它是一个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()));
// 如你所见它是一个mapkey由分组idvalue由用户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);

View File

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

View File

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

View File

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

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.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>

View File

@ -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
/**

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.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>