From 4a44155eb4b8a94676a9081173de012347b4b897 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 4 Feb 2026 10:05:27 +0800 Subject: [PATCH] add bmh --- .../cn/hutool/v7/jmh/json/JSONJmhData.java | 72 ++++++++++++++ .../cn/hutool/v7/jmh/json/deserializeJmh.java | 98 +++++++++++++++++++ .../src/main/java/cn/hutool/v7/json/JSON.java | 9 ++ 3 files changed, 179 insertions(+) create mode 100644 hutool-jmh/src/test/java/cn/hutool/v7/jmh/json/deserializeJmh.java diff --git a/hutool-jmh/src/test/java/cn/hutool/v7/jmh/json/JSONJmhData.java b/hutool-jmh/src/test/java/cn/hutool/v7/jmh/json/JSONJmhData.java index e307f5c90e..d22340527f 100644 --- a/hutool-jmh/src/test/java/cn/hutool/v7/jmh/json/JSONJmhData.java +++ b/hutool-jmh/src/test/java/cn/hutool/v7/jmh/json/JSONJmhData.java @@ -16,10 +16,19 @@ package cn.hutool.v7.jmh.json; +import cn.hutool.v7.core.io.file.FileUtil; +import cn.hutool.v7.core.lang.Console; +import cn.hutool.v7.json.JSONArray; +import cn.hutool.v7.json.JSONObject; import lombok.Data; +import org.junit.jupiter.api.Test; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.util.Date; import java.util.List; +import java.util.Random; public class JSONJmhData { public static String jsonStr = """ @@ -57,4 +66,67 @@ public class JSONJmhData { private Long subNumber; private boolean subBoolean; } + + @Test + void generateMookJsonTest() { + generateMookJson(); + } + + // 配置项:可调整以生成不同大小的 JSON + private static final int DATA_ITEM_COUNT = 100_000; // 数据条目数(10万条,约几十MB;100万条约几百MB) + public static final String OUTPUT_FILE_PATH = "D:/test/big_test_data_hutool.json"; // 输出文件路径 + + private static File generateMookJson() { + // 1. 初始化 JSON 数组(存储核心数据) + final JSONArray jsonArray = new JSONArray(); + final Random random = new Random(); + + Console.log("开始生成 JSON 数据..."); + final long startTime = System.currentTimeMillis(); + + // 2. 循环生成模拟数据条目 + for (int i = 0; i < DATA_ITEM_COUNT; i++) { + final JSONObject dataItem = new JSONObject(); + // 基础字段(多种数据类型,贴近真实场景) + dataItem.putValue("id", i + 1); + dataItem.putValue("userName", "test_user_" + (i % 1000)); // 避免用户名重复过多 + dataItem.putValue("age", random.nextInt(50) + 20); // 年龄 20-69 + dataItem.putValue("email", "test_" + i + "@example.com"); + dataItem.putValue("phone", "138" + String.format("%08d", random.nextInt(99999999))); + dataItem.putValue("isVip", random.nextBoolean()); // 布尔类型 + dataItem.putValue("balance", random.nextDouble() * 10000); // 浮点类型(余额) + dataItem.putValue("registerTime", "202" + (random.nextInt(6)) + "-" + + String.format("%02d", random.nextInt(12) + 1) + "-" + + String.format("%02d", random.nextInt(28) + 1)); // 日期字符串 + + // 嵌套对象(增加 JSON 复杂度,更贴近真实测试场景) + final JSONObject address = new JSONObject(); + address.putValue("province", "省份_" + (random.nextInt(34) + 1)); + address.putValue("city", "城市_" + (random.nextInt(10) + 1)); + address.putValue("detail", "详细地址_" + i); + dataItem.putValue("address", address); + + // 3. 将单个条目添加到 JSON 数组 + jsonArray.add(dataItem); + + // 可选:每生成 1 万条打印进度,避免等待焦虑 + if ((i + 1) % 10000 == 0) { + Console.log("已生成 " + (i + 1) + " 条数据"); + } + } + + // 4. 流式写入文件(避免内存积压) + final File file = FileUtil.file(OUTPUT_FILE_PATH); + try (final FileWriter fileWriter = new FileWriter(file)) { + jsonArray.write(fileWriter); // Hutool 内置流式写入,无需手动拼接 + final long endTime = System.currentTimeMillis(); + Console.log("JSON 文件生成完成!"); + Console.log("文件路径:" + OUTPUT_FILE_PATH); + Console.log("耗时:" + (endTime - startTime) + " 毫秒"); + Console.log("数据条目数:" + DATA_ITEM_COUNT); + } catch (final IOException e) { + System.err.println("文件写入失败:" + e.getMessage()); + } + return file; + } } diff --git a/hutool-jmh/src/test/java/cn/hutool/v7/jmh/json/deserializeJmh.java b/hutool-jmh/src/test/java/cn/hutool/v7/jmh/json/deserializeJmh.java new file mode 100644 index 0000000000..28b103cc79 --- /dev/null +++ b/hutool-jmh/src/test/java/cn/hutool/v7/jmh/json/deserializeJmh.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2013-2026 Hutool Team. + * + * 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.jmh.json; + +import cn.hutool.v7.core.io.file.FileUtil; +import cn.hutool.v7.json.JSON; +import cn.hutool.v7.json.JSONUtil; +import cn.hutool.v7.json.engine.JSONEngine; +import cn.hutool.v7.json.engine.JSONEngineFactory; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonElement; +import org.openjdk.jmh.annotations.*; + +import java.io.IOException; +import java.io.Reader; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime)//每次执行平均花费时间 +@Warmup(iterations = 5, time = 1) //预热5次调用 +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) // 执行5次,每次1秒 +@Threads(1) //单线程 +@Fork(1) // +@OutputTimeUnit(TimeUnit.NANOSECONDS) // 单位:纳秒 +@State(Scope.Benchmark) // 共享域 +public class deserializeJmh { + + private JSONEngine jacksonEngine; + private JSONEngine gsonEngine; + private JSONEngine fastJSONEngine; + private JSONEngine hutoolEngine; + + @Setup + public void setup() { + jacksonEngine = JSONEngineFactory.createEngine("jackson"); + gsonEngine = JSONEngineFactory.createEngine("gson"); + fastJSONEngine = JSONEngineFactory.createEngine("fastjson"); + hutoolEngine = JSONEngineFactory.createEngine("hutool"); + } + + @Benchmark + public void jacksonJmh() { + try(final Reader jsonFileReader = FileUtil.getUtf8Reader(JSONJmhData.OUTPUT_FILE_PATH)){ + jacksonEngine.deserialize(jsonFileReader, JsonNode.class); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Benchmark + public void gsonJmh() { + try(final Reader jsonFileReader = FileUtil.getUtf8Reader(JSONJmhData.OUTPUT_FILE_PATH)){ + gsonEngine.deserialize(jsonFileReader, JsonElement.class); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Benchmark + public void fastJSONJmh() { + try(final Reader jsonFileReader = FileUtil.getUtf8Reader(JSONJmhData.OUTPUT_FILE_PATH)){ + fastJSONEngine.deserialize(jsonFileReader, com.alibaba.fastjson2.JSON.class); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Benchmark + public void hutoolJSONJmh() { + try(final Reader jsonFileReader = FileUtil.getUtf8Reader(JSONJmhData.OUTPUT_FILE_PATH)){ + hutoolEngine.deserialize(jsonFileReader, JSON.class); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Benchmark + public void hutoolJSONParseJmh() { + try(final Reader jsonFileReader = FileUtil.getUtf8Reader(JSONJmhData.OUTPUT_FILE_PATH)){ + JSONUtil.parse(jsonFileReader); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/hutool-json/src/main/java/cn/hutool/v7/json/JSON.java b/hutool-json/src/main/java/cn/hutool/v7/json/JSON.java index 12708c069d..bd05816192 100644 --- a/hutool-json/src/main/java/cn/hutool/v7/json/JSON.java +++ b/hutool-json/src/main/java/cn/hutool/v7/json/JSON.java @@ -60,6 +60,15 @@ public interface JSON extends Serializable { */ void write(JSONWriter writer) throws JSONException; + /** + * 输出JSON到{@link Appendable} + * + * @param appendable 追加器 + */ + default void write(final Appendable appendable){ + write(getFactory().ofWriter(appendable)); + } + /** * 获取JSON配置 *