JSON(Vostok.Util / VKJsonProvider)

序列化 / 反序列化

User user = new User(1L, "Alice");

// 对象 → JSON 字符串
String json = Vostok.Util.toJson(user);

// JSON 字符串 → 对象
User u = Vostok.Util.fromJson(json, User.class);

// JSON 字符串 → Map(通用结构)
Map<String, Object> map = Vostok.Util.fromJson(json, Map.class);
内置 JSON Provider 能力范围
内置 Provider 支持 POJO(反射)、MapList、数组、基本类型、枚举的序列化与反序列化。 泛型容器(如 List<User>)请使用 Jackson 等外部 Provider,或通过 fromJson(json, Map.class) 先解析为 Map 再手动转换。

切换 JSON Provider

// 注册并切换到 Jackson Provider
Vostok.Util.registerJsonProvider(new JacksonVKJsonProvider());
Vostok.Util.useJsonProvider("jackson");

// 恢复内置 Provider
Vostok.Util.resetDefaultJsonProvider();

// 查询
String current = Vostok.Util.currentJsonProviderName();
List<String> all = Vostok.Util.jsonProviderNames();

字符串工具(VKStrings)

空值检查

VKStrings.isEmpty(null);                       // true
VKStrings.isEmpty("");                          // true
VKStrings.isBlank("  ");                        // true
VKStrings.trimToNull("  ");                     // null
VKStrings.trimToEmpty(null);                    // ""
VKStrings.defaultIfBlank("  ", "default");     // "default"

分割与拼接

// 快速字符分割,返回 String[](两遍扫描,无正则开销)
String[] parts = VKStrings.fastSplit("a,b,c", ',');   // ["a", "b", "c"]

// 分割并返回 List
List<String> list = VKStrings.splitToList("a,b,c", ',');

// 拼接(ThreadLocal StringBuilder,无额外分配)
String s1 = VKStrings.join(',', "a", "b", "c");       // "a,b,c"
String s2 = VKStrings.join(',', myList);               // Iterable 重载

命名风格转换

VKStrings.camelToSnake("userName");   // "user_name"
VKStrings.snakeToCamel("user_name");  // "userName"
VKStrings.kebabToCamel("user-name");  // "userName"

大小写无关比较(ASCII 优化)

VKStrings.equalsIgnoreCaseAscii("GET", "get");             // true
VKStrings.startsWithIgnoreCaseAscii("Content-Type", "content"); // true
VKStrings.containsIgnoreCaseAscii("Hello World", "world");   // true
VKStrings.wildcardMatch("user.json", "*.json");             // true(* 和 ? 通配)

截断与清理

// 按 Unicode 码点截断,正确处理中文/emoji(不会截断代理对)
VKStrings.truncateByCodePoint("Hello 世界", 8);

// 截断并添加省略号
VKStrings.ellipsis("很长的标题文字", 5);   // "很长的标…"(最多 5 个码点)

// 越界安全的 substring
VKStrings.safeSubstring("Hello", 0, 100);  // "Hello",不抛异常

VKStrings.removeWhitespace("a b  c");      // "abc"
VKStrings.collapseSpaces("a  b   c");      // "a b c"
VKStrings.stripControlChars(input);          // 移除 ASCII 控制字符(保留 \n \r \t)
VKStrings.replaceChar("a/b/c", '/', '.');  // "a.b.c"

URL 编解码 / 字节转换

String enc = VKStrings.urlEncodeUtf8("hello world");  // "hello+world"
String dec = VKStrings.urlDecodeUtf8("hello+world");  // "hello world"

byte[] bytes = VKStrings.toUtf8Bytes("你好");
String str   = VKStrings.fromUtf8Bytes(bytes);

类型解析(带默认值)

int     n = VKStrings.toIntOrDefault("42", 0);         // 42
long    l = VKStrings.toLongOrDefault("abc", -1L);    // -1(解析失败返回默认值)
boolean b = VKStrings.toBoolOrDefault("yes", false);  // true(支持 true/1/yes/on)

ID 生成(VKIds)

// UUID(带连字符,标准格式)
String id = VKIds.uuid();   // "550e8400-e29b-41d4-a716-446655440000"

// 随机字母数字字符串(大小写字母 + 数字,使用 SecureRandom)
String token = VKIds.randomAlphaNum(32);

// 固定长度 Trace ID:13位时间戳 + "-" + 16位随机数,总长 30 字符
String traceId = VKIds.traceId();   // 如 "019c075c4abc-3f8a2b1c4e5d6f7a"
traceId 格式说明
VKIds.traceId() 返回固定 30 字符,格式为 <13位十六进制时间戳>-<16位十六进制随机数>。 时间戳部分高位补零,在 2527 年前始终为 13 位。随机部分按 64 位无符号十六进制输出,始终为 16 位。 Trace ID 仅用于可观测性,使用 ThreadLocalRandom 而非 SecureRandom,高并发下无竞争。 randomAlphaNum 用于 token/密钥场景,仍使用 SecureRandom

时间工具(VKTimes)

// 当前时间戳
long ms  = VKTimes.nowEpochMs();   // System.currentTimeMillis()
long sec = VKTimes.nowEpochSec();  // epoch 秒

// 格式化(DateTimeFormatter 按 pattern+zone 缓存,高频调用无重复构造开销)
String str = VKTimes.formatInstant(
    Instant.now(),
    ZoneId.of("Asia/Shanghai"),
    "yyyy-MM-dd HH:mm:ss"
);

// 解析(pattern 为空时默认 "yyyy-MM-dd HH:mm:ss")
Instant t = VKTimes.parseInstant(
    "2024-01-01 12:00:00",
    ZoneId.of("Asia/Shanghai"),
    "yyyy-MM-dd HH:mm:ss"
);

// 纯日期 pattern 也支持(自动降级为 LocalDate.parse,补零时)
Instant t2 = VKTimes.parseInstant("2024-01-01", ZoneId.of("UTC"), "yyyy-MM-dd");

// 当天起止时间(精确到纳秒)
Instant start = VKTimes.startOfDay(Instant.now(), ZoneId.of("Asia/Shanghai"));
Instant end   = VKTimes.endOfDay(Instant.now(), ZoneId.of("Asia/Shanghai"));
ZoneId 传 null
所有接受 ZoneId 参数的方法,传入 null 时自动使用 ZoneId.systemDefault()

集合工具(VKCollections)

工厂方法(预设容量,避免扩容)

// 按预期元素数分配初始容量(自动计算 HashMap loadFactor 补偿)
HashMap<String, Integer> map  = VKCollections.newHashMapWithExpectedSize(100);
LinkedHashMap<K, V>    lmap = VKCollections.newLinkedHashMapWithExpectedSize(50);
HashSet<String>        set  = VKCollections.newHashSetWithExpectedSize(50);
ConcurrentHashMap<K,V> chm = VKCollections.newConcurrentHashMapWithExpectedSize(200);
ArrayList<String>      list = VKCollections.newArrayListWithExpectedSize(64);

安全访问(不抛越界异常)

String v = VKCollections.safeGet(list, 5, "default");  // 越界返回默认值
String f = VKCollections.safeFirst(list, null);
String l = VKCollections.safeLast(list, null);
List<String> sub = VKCollections.safeSubList(list, 2, 10);  // 自动裁剪边界

String val = VKCollections.getOrDefault(map, "key", "fallback");  // map 为 null 安全

集合运算

// 去重(保持首次出现顺序)
List<String> unique = VKCollections.distinctPreserveOrder(list);

// 按 key 函数去重(保留首次出现)
List<User> deduped = VKCollections.distinctBy(users, User::getId);

// 集合运算(均保持顺序,返回新 List)
List<T> union  = VKCollections.union(a, b);       // 并集,去重
List<T> inter  = VKCollections.intersect(a, b);   // 交集
List<T> diff   = VKCollections.difference(a, b);  // 差集(a 中有、b 中没有)

函数式操作

List<User> adults  = VKCollections.filter(users, u -> u.getAge() >= 18);
List<String> names = VKCollections.map(users, User::getName);
List<Tag> tags     = VKCollections.flatMap(posts, Post::getTags);
List<User> noNulls = VKCollections.compact(users);  // 移除 null 元素

// 统计
long    cnt = VKCollections.count(users, u -> u.getAge() > 18);
boolean any = VKCollections.anyMatch(users, u -> u.isAdmin());
boolean all = VKCollections.allMatch(users, u -> u.isActive());

索引与分组

// 按 key 建立索引(重复 key 取后者)
Map<Long, User> byId = VKCollections.indexBy(users, User::getId);

// 按 key 分组
Map<String, List<User>> byDept = VKCollections.groupBy(users, User::getDept);

// 转为 Map(支持重复 key 合并)
Map<Long, String> nameById = VKCollections.toMap(
    users, User::getId, User::getName, (a, b) -> a  // 重复 key 时保留旧值
);

分页与分块

// 按固定大小分块(chunked 和 partition 等价)
List<List<User>> batches = VKCollections.chunked(users, 100);

// 按页码取数据(pageNo 从 1 开始)
List<User> page = VKCollections.page(users, 2, 20);  // 第2页,每页20条

其他工具

boolean hit = VKCollections.containsAny(setA, setB);  // 是否有交集(自动小集合嵌套大集合)

List<String> rev  = VKCollections.reverseNew(list);        // 返回倒序副本(不修改原列表)
List<Integer> rep = VKCollections.repeat(0, 5);           // [0, 0, 0, 0, 0]
VKCollections.swap(list, 0, 3);                           // 原地交换两个元素

// 不可变副本(底层 List.copyOf / Set.copyOf / Map.copyOf)
List<T> immList = VKCollections.immutableCopy(list);
Set<T>  immSet  = VKCollections.immutableCopy(set);
Map<K,V> immMap = VKCollections.immutableCopy(map);

编解码(VKCodecs)

Base64

// 编码
String b64 = VKCodecs.base64Encode(someBytes);       // byte[] → Base64 字符串
String b64 = VKCodecs.base64Encode("hello");         // String → Base64 字符串

// 解码
byte[] raw = VKCodecs.base64DecodeToBytes(b64);
String str = VKCodecs.base64DecodeToString(b64);

十六进制

String hex   = VKCodecs.hexEncode(new byte[]{0x0f, 0xff});  // "0fff"(小写)
byte[] bytes = VKCodecs.hexDecode("0fff");

摘要 / 校验和

// MD5 和 SHA-256 使用 ThreadLocal MessageDigest,高频调用无实例构造开销
String md5    = VKCodecs.md5Hex("hello");     // 32 位小写十六进制
String sha256 = VKCodecs.sha256Hex("hello");  // 64 位小写十六进制

// CRC32 校验和(非加密用途,如数据完整性校验)
long crc = VKCodecs.crc32(someBytes);

API 速查

方法说明
Vostok.UtiltoJson(obj)对象序列化为 JSON
fromJson(json, Class)JSON 反序列化
registerJsonProvider(provider)注册自定义 JSON Provider
useJsonProvider(name)切换当前 JSON Provider
VKStringsisEmpty / isBlank空/空白检查
trimToNull / trimToEmpty / defaultIfBlank空值转换
fastSplit(str, char) → String[]快速字符分割
splitToList(str, char) → List分割并返回 List
join(char, Object...)拼接(ThreadLocal 复用)
camelToSnake / snakeToCamel / kebabToCamel命名风格转换
wildcardMatch(val, pattern)Glob 通配匹配(* 和 ?)
ellipsis(str, maxCodePoints)安全截断并加省略号
toIntOrDefault / toLongOrDefault / toBoolOrDefault字符串解析(带默认值)
VKIdsuuid()随机 UUID(带连字符)
randomAlphaNum(len)随机字母数字串(SecureRandom)
traceId()固定长度 trace ID,共 30 字符
VKTimesnowEpochMs() / nowEpochSec()当前时间戳
formatInstant(instant, zone, pattern)Instant 格式化(formatter 缓存)
parseInstant(str, zone, pattern)字符串解析为 Instant(支持纯日期 pattern)
startOfDay / endOfDay(instant, zone)当天起止 Instant
VKCollectionsnewHashMapWithExpectedSize(n)预设容量,避免 rehash
safeGet / safeFirst / safeLast(list, ...)越界安全访问
distinctPreserveOrder / distinctBy去重(保持顺序)
union / intersect / difference集合运算
filter / map / flatMap / compact函数式操作
indexBy / groupBy / toMap索引与分组
chunked / page分块与分页
containsAny / anyMatch / allMatch / count条件检查与统计
VKCodecsbase64Encode / base64DecodeToBytes / base64DecodeToStringBase64 编解码
hexEncode / hexDecode十六进制编解码
md5Hex / sha256Hex摘要(ThreadLocal 复用,低延迟)
crc32(bytes)CRC32 校验和