🛠️ Vostok.Util
Util 工具集模块
通用工具门面,提供 JSON 序列化(可切换 Provider)、字符串处理、分布式 ID 生成、时间格式化、集合操作、编解码等常用能力。无需 init,开箱即用。
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(反射)、Map、List、数组、基本类型、枚举的序列化与反序列化。
泛型容器(如 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.Util | toJson(obj) | 对象序列化为 JSON |
fromJson(json, Class) | JSON 反序列化 | |
registerJsonProvider(provider) | 注册自定义 JSON Provider | |
useJsonProvider(name) | 切换当前 JSON Provider | |
VKStrings | isEmpty / 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 | 字符串解析(带默认值) | |
VKIds | uuid() | 随机 UUID(带连字符) |
randomAlphaNum(len) | 随机字母数字串(SecureRandom) | |
traceId() | 固定长度 trace ID,共 30 字符 | |
VKTimes | nowEpochMs() / nowEpochSec() | 当前时间戳 |
formatInstant(instant, zone, pattern) | Instant 格式化(formatter 缓存) | |
parseInstant(str, zone, pattern) | 字符串解析为 Instant(支持纯日期 pattern) | |
startOfDay / endOfDay(instant, zone) | 当天起止 Instant | |
VKCollections | newHashMapWithExpectedSize(n) | 预设容量,避免 rehash |
safeGet / safeFirst / safeLast(list, ...) | 越界安全访问 | |
distinctPreserveOrder / distinctBy | 去重(保持顺序) | |
union / intersect / difference | 集合运算 | |
filter / map / flatMap / compact | 函数式操作 | |
indexBy / groupBy / toMap | 索引与分组 | |
chunked / page | 分块与分页 | |
containsAny / anyMatch / allMatch / count | 条件检查与统计 | |
VKCodecs | base64Encode / base64DecodeToBytes / base64DecodeToString | Base64 编解码 |
hexEncode / hexDecode | 十六进制编解码 | |
md5Hex / sha256Hex | 摘要(ThreadLocal 复用,低延迟) | |
crc32(bytes) | CRC32 校验和 |