Java中如何从JSON文件指定索引开始读取数据?
高效从指定索引开始解析JSON数组的方案
你的问题很典型:当处理百万级别的JSON数组时,全量解析再用subList截取会导致内存占用过高和解析效率低下——毕竟要把所有对象都加载到内存里,完全没必要。解决这个问题的核心是用流式解析,也就是逐个读取JSON元素,跳过前99个,只处理从第100个开始的内容,全程不需要把整个数组加载到内存。
下面给你两种实用的实现方案,分别基于目前最常用的两个JSON库:
方案一:使用Jackson流式API(推荐)
Jackson是Java生态中性能最好的JSON库之一,它的流式API专门为处理大文件设计,内存占用极低。
代码示例
import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.FileReader; import java.io.IOException; public class JsonStreamProcessor { public static void processJsonFromIndex(int startIndex) throws IOException { // 初始化Jackson的核心组件 JsonFactory factory = new JsonFactory(); ObjectMapper mapper = new ObjectMapper(); // 用try-with-resources自动关闭资源 try (JsonParser parser = factory.createParser(new FileReader("D:\\2018-4-21.json"))) { // 先定位到JSON数组的起始位置 if (parser.nextToken() != JsonToken.START_ARRAY) { throw new IOException("文件内容不是有效的JSON数组"); } int currentItemIndex = 0; JsonToken currentToken; while ((currentToken = parser.nextToken()) != null) { // 数组结束就退出循环 if (currentToken == JsonToken.END_ARRAY) { break; } currentItemIndex++; // 跳过前99个元素(如果startIndex是100,对应索引0-99的元素) if (currentItemIndex < startIndex) { // 直接跳过当前对象的所有子节点,不解析 parser.skipChildren(); continue; } // 解析当前JSON对象并处理 JsonNode jsonObject = mapper.readTree(parser); // 这里替换成你的业务逻辑,比如获取字段 String targetField = jsonObject.get("che...").asText(); System.out.println("处理第" + currentItemIndex + "个对象:" + targetField); } } } public static void main(String[] args) throws IOException { // 从第100个元素开始处理(注意:这里的索引是从1开始计数,如果你的原代码subList(100, ...)是跳过前100个(索引0-99),这里传100正好对应) processJsonFromIndex(100); } }
方案优势
- 内存占用极小:只在处理当前对象时加载其数据,不会把百万级数组全部塞进内存
- 解析效率高:跳过不需要的元素时直接跳过子节点,无需解析内容
- 代码简洁:Jackson的流式API封装得很友好,容易维护
方案二:使用JSON.simple的流式处理(适配你的原有库)
如果你因为项目依赖限制必须使用JSON.simple,也可以通过操作token流来实现跳过逻辑,不过这个库的流式API不够完善,代码会繁琐一些。
代码示例
import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.json.simple.parser.Token; import org.json.simple.parser.TokenType; import org.json.simple.parser.Yylex; import java.io.FileReader; import java.io.IOException; public class JsonSimpleStreamProcessor { public static void processJsonFromIndex(int startIndex) throws IOException, ParseException { JSONParser parser = new JSONParser(); try (FileReader reader = new FileReader("D:\\2018-4-21.json")) { Yylex lexer = new Yylex(reader); Token currentToken; // 先定位到JSON数组的左括号 while ((currentToken = lexer.nextToken()) != null) { if (currentToken.type == TokenType.LEFT_BRACKET) { break; } } int currentItemIndex = 0; while ((currentToken = lexer.nextToken()) != null) { // 数组结束就退出 if (currentToken.type == TokenType.RIGHT_BRACKET) { break; } currentItemIndex++; if (currentItemIndex < startIndex) { // 跳过当前对象:需要计数大括号,直到匹配到闭合的右括号 int braceCount = 1; while ((currentToken = lexer.nextToken()) != null) { if (currentToken.type == TokenType.LEFT_BRACE) { braceCount++; } else if (currentToken.type == TokenType.RIGHT_BRACE) { braceCount--; if (braceCount == 0) { break; } } } continue; } // 解析当前对象并处理 JSONObject jsonObject = (JSONObject) parser.parse(lexer); String targetField = (String) jsonObject.get("che..."); System.out.println("处理第" + currentItemIndex + "个对象:" + targetField); } } } public static void main(String[] args) throws IOException, ParseException { processJsonFromIndex(100); } }
注意事项
JSON.simple的token处理需要自己手动计数大括号,容易出错,而且解析效率不如Jackson,所以优先推荐方案一。
内容的提问来源于stack exchange,提问作者Gopi N
相关产品推荐
相关产品推荐

