如何将大型 JSON 文件分割成块并使用 GSON 进行排序

2024-01-01

我有一个巨大的 JSON 文件,名为 Something.json。文件大小为 20 MB。我正在用 GSON 读这篇文章。它可以在标准 Android Nexus 5X 上读取。

Json 示例:

[
    {"country":"UA","name":"Hurzuf","_id":707860,"coord":{"lon":34.283333,"lat":44.549999}},
    {"country":"UA","name":"Il’ichëvka","_id":707716,"coord":{"lon":34.383331,"lat":44.666668}},
    {"country":"BG","name":"Rastnik","_id":727762,"coord":{"lon":25.283331,"lat":41.400002}}
...
]

Code:

@Override
protected ArrayList<City> doInBackground(File... files) {
    ArrayList<City> cities = new ArrayList<>();
    try {
        InputStream is = new FileInputStream(files[0]);
        JsonReader reader = new JsonReader(new InputStreamReader(is, "UTF-8"));
        reader.beginArray();
        while (reader.hasNext()) {
            City city = new Gson().fromJson(reader, City.class);
            cities.add(city);
        }
        reader.endArray();
        reader.close();
    } catch (Exception e) {
        mResult.onFinish(cities, e.getMessage());
    }

    Collections.sort(cities, (o1, o2) -> o1.getName().compareTo(o2.getName()));
    mResult.onFinish(cities, CityService.SUCCESS);
    return cities;
}

使用的库:

com.google.code.gson:gson:2.8.0

它需要从安卓API 16直到最晚。

我需要将其读入 mCities,并按城市名称的字母顺序对其进行排序。现在这需要 3 分钟,并且必须在大约 10 秒内完成。我的方法是将 json 文件分成 10 个较小的块,读取它们,连接并排序它们。

所以我的问题是:如何将文件分成更小的块,这是解决这个问题的正确方法吗?

文件链接:http://www.jimclermonts.nl/docs/cities.json http://www.jimclermonts.nl/docs/cities.json


我本身基本上从不进行 Android 编码,但我有一些笔记和可能的想法供您参考,因为这是纯Java。 你的读者会very阅读每个元素时工作量过大。 首先,你不需要创建Gson每次你需要的时候:

  • 它是不可变的且线程安全的。
  • 创建它的成本相对较高。
  • 实例化一个Gson实例也命中堆more执行然后垃圾收集的时间。

接下来,Gson 中的仅反序列化和 JSON 流读取之间存在差异:第一个可能在底层使用重型类型适配器组合,而后者可以简单地逐个解析 JSON 文档。 话虽如此,您在读取 ​​JSON 流时可以获得更好的性能:众所周知,您的 JSON 文件具有非常严格的结构,因此高级解析器可以更简单地实现。

假设一个简单的测试套件针对您的问题有不同的实现:

数据对象

城市.java

final class City {

    @SerializedName("_id")
    final int id;

    @SerializedName("country")
    final String country;

    @SerializedName("name")
    final String name;

    @SerializedName("coord")
    final Coordinates coordinates;

    private City(final int id, final String country, final String name, final Coordinates coordinates) {
        this.id = id;
        this.country = country;
        this.name = name;
        this.coordinates = coordinates;
    }

    static City of(final int id, final String country, final String name, final Coordinates coordinates) {
        return new City(id, country, name, coordinates);
    }

    @Override
    public boolean equals(final Object o) {
        if ( this == o ) {
            return true;
        }
        if ( o == null || getClass() != o.getClass() ) {
            return false;
        }
        final City that = (City) o;
        return id == that.id;
    }

    @Override
    public int hashCode() {
        return id;
    }

    @SuppressWarnings("ConstantConditions")
    public static int compareByName(final City city1, final City city2) {
        return city1.name.compareTo(city2.name);
    }

}

坐标.java

final class Coordinates {

    @SerializedName("lat")
    final double latitude;

    @SerializedName("lon")
    final double longitude;

    private Coordinates(final double latitude, final double longitude) {
        this.latitude = latitude;
        this.longitude = longitude;
    }

    static Coordinates of(final double latitude, final double longitude) {
        return new Coordinates(latitude, longitude);
    }

    @Override
    public boolean equals(final Object o) {
        if ( this == o ) {
            return true;
        }
        if ( o == null || getClass() != o.getClass() ) {
            return false;
        }
        final Coordinates that = (Coordinates) o;
        return Double.compare(that.latitude, latitude) == 0
                && Double.compare(that.longitude, longitude) == 0;
    }

    @Override
    public int hashCode() {
        final long latitudeBits = Double.doubleToLongBits(latitude);
        final long longitudeBits = Double.doubleToLongBits(longitude);
        final int latitudeHash = (int) (latitudeBits ^ latitudeBits >>> 32);
        final int longitudeHash = (int) (longitudeBits ^ longitudeBits >>> 32);
        return 31 * latitudeHash + longitudeHash;
    }

}

测试基础设施

ITest.java

interface ITest {

    @Nonnull
    default String getName() {
        return getClass().getSimpleName();
    }

    @Nonnull
    Collection<City> test(@Nonnull JsonReader jsonReader)
            throws IOException;

}

main

    public static void main(final String... args)
            throws IOException {
        final Iterable<ITest> tests = ImmutableList.of(
                FirstTest.get(),
                ReadAsWholeListTest.get(),
                ReadAsWholeTreeSetTest.get(),
                ReadJsonStreamIntoListTest.get(),
                ReadJsonStreamIntoTreeSetTest.get(),
                ReadJsonStreamIntoListChunksTest.get()
        );
        for ( int i = 0; i < 3; i++ ) {
            for ( final ITest test : tests ) {
                try ( final ZipInputStream zipInputStream = new ZipInputStream(Resources.getPackageResourceInputStream(Q49273660.class, "cities.json.zip")) ) {
                    for ( ZipEntry zipEntry = zipInputStream.getNextEntry(); zipEntry != null; zipEntry = zipInputStream.getNextEntry() ) {
                        if ( zipEntry.getName().equals("cities.json") ) {
                            final JsonReader jsonReader = new JsonReader(new InputStreamReader(zipInputStream)); // do not close
                            System.out.printf("%1$35s : ", test.getName());
                            final Stopwatch stopwatch = Stopwatch.createStarted();
                            final Collection<City> cities = test.test(jsonReader);
                            System.out.printf("in %d ms with %d elements\n", stopwatch.elapsed(TimeUnit.MILLISECONDS), cities.size());
                            assertSorted(cities, City::compareByName);
                        }
                    }
                }
            }
            System.out.println("--------------------");
        }
    }

    private static <E> void assertSorted(final Iterable<? extends E> iterable, final Comparator<? super E> comparator) {
        final Iterator<? extends E> iterator = iterable.iterator();
        if ( !iterator.hasNext() ) {
            return;
        }
        E a = iterator.next();
        if ( !iterator.hasNext() ) {
            return;
        }
        do {
            final E b = iterator.next();
            if ( comparator.compare(a, b) > 0 ) {
                throw new AssertionError(a + " " + b);
            }
            a = b;
        } while ( iterator.hasNext() );
    }

Tests

FirstTest.java

这是最慢的一个。 这只是你的问题对测试的适应。

final class FirstTest
        implements ITest {

    private static final ITest instance = new FirstTest();

    private FirstTest() {
    }

    static ITest get() {
        return instance;
    }

    @Nonnull
    @Override
    public List<City> test(@Nonnull final JsonReader jsonReader)
            throws IOException {
        jsonReader.beginArray();
        final List<City> cities = new ArrayList<>();
        while ( jsonReader.hasNext() ) {
            final City city = new Gson().fromJson(jsonReader, City.class);
            cities.add(city);
        }
        jsonReader.endArray();
        cities.sort(City::compareByName);
        return cities;
    }

}

ReadAsWholeListTest.java

这很可能是您实现它的方式。 它不是获胜者,但它是最简单的一个,并且它使用默认排序。

final class ReadAsWholeListTest
        implements ITest {

    private static final ITest instance = new ReadAsWholeListTest();

    private ReadAsWholeListTest() {
    }

    static ITest get() {
        return instance;
    }

    private static final Gson gson = new Gson();

    private static final Type citiesListType = new TypeToken<List<City>>() {
    }.getType();

    @Nonnull
    @Override
    public List<City> test(@Nonnull final JsonReader jsonReader) {
        final List<City> cities = gson.fromJson(jsonReader, citiesListType);
        cities.sort(City::compareByName);
        return cities;
    }

}

ReadAsWholeTreeSetTest.java

另一个想法,如果你不绑定到列表,是使用已经排序的集合,例如TreeSet。 因为我不知道是否有办法指定一个新的TreeSet比较器机制Gson,它必须使用自定义类型适配器工厂(但这不是必需的,如果City已经可以通过名称进行比较,但是它不灵活)。

final class ReadAsWholeTreeSetTest
        implements ITest {

    private static final ITest instance = new ReadAsWholeTreeSetTest();

    private ReadAsWholeTreeSetTest() {
    }

    static ITest get() {
        return instance;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static final TypeToken<TreeSet<?>> rawTreeSetType = (TypeToken) TypeToken.get(TreeSet.class);

    private static final Map<Type, Comparator<?>> comparatorsRegistry = ImmutableMap.of(
            City.class, (Comparator<City>) City::compareByName
    );

    private static final Gson gson = new GsonBuilder()
            .registerTypeAdapterFactory(new TypeAdapterFactory() {
                @Override
                public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
                    if ( !TreeSet.class.isAssignableFrom(typeToken.getRawType()) ) {
                        return null;
                    }
                    final Type elementType = ((ParameterizedType) typeToken.getType()).getActualTypeArguments()[0];
                    @SuppressWarnings({ "rawtypes", "unchecked" })
                    final Comparator<Object> comparator = (Comparator) comparatorsRegistry.get(elementType);
                    if ( comparator == null ) {
                        return null;
                    }
                    final TypeAdapter<TreeSet<?>> originalTreeSetTypeAdapter = gson.getDelegateAdapter(this, rawTreeSetType);
                    final TypeAdapter<?> originalElementTypeAdapter = gson.getDelegateAdapter(this, TypeToken.get(elementType));
                    final TypeAdapter<TreeSet<Object>> treeSetTypeAdapter = new TypeAdapter<TreeSet<Object>>() {
                        @Override
                        public void write(final JsonWriter jsonWriter, final TreeSet<Object> treeSet)
                                throws IOException {
                            originalTreeSetTypeAdapter.write(jsonWriter, treeSet);
                        }

                        @Override
                        public TreeSet<Object> read(final JsonReader jsonReader)
                                throws IOException {
                            jsonReader.beginArray();
                            final TreeSet<Object> elements = new TreeSet<>(comparator);
                            while ( jsonReader.hasNext() ) {
                                final Object element = originalElementTypeAdapter.read(jsonReader);
                                elements.add(element);
                            }
                            return elements;
                        }
                    }.nullSafe();
                    @SuppressWarnings({ "rawtypes", "unchecked" })
                    final TypeAdapter<T> castTreeSetTypeAdapter = (TypeAdapter<T>) treeSetTypeAdapter;
                    return castTreeSetTypeAdapter;
                }
            })
            .create();

    private static final Type citiesSetType = new TypeToken<TreeSet<City>>() {
    }.getType();

    @Nonnull
    @Override
    public Set<City> test(@Nonnull final JsonReader jsonReader) {
        return gson.fromJson(jsonReader, citiesSetType);
    }

}

JSON 流读取器测试

下面的类是一个特殊的读取器测试,它使用读取城市 JSON 的简化策略。

AbstractJsonStreamTest.java

它可能是尽可能简单的(就JSON结构分析而言),并且它要求JSON文档非常严格。

abstract class AbstractJsonStreamTest
        implements ITest {

    protected static void read(final JsonReader jsonReader, final Consumer<? super City> cityConsumer)
            throws IOException {
        jsonReader.beginArray();
        while ( jsonReader.hasNext() ) {
            jsonReader.beginObject();
            require(jsonReader, "country");
            final String country = jsonReader.nextString();
            require(jsonReader, "name");
            final String name = jsonReader.nextString();
            require(jsonReader, "_id");
            final int id = jsonReader.nextInt();
            require(jsonReader, "coord");
            jsonReader.beginObject();
            require(jsonReader, "lon");
            final double longitude = jsonReader.nextDouble();
            require(jsonReader, "lat");
            final double latitude = jsonReader.nextDouble();
            jsonReader.endObject();
            jsonReader.endObject();
            final City city = City.of(id, country, name, Coordinates.of(latitude, longitude));
            cityConsumer.accept(city);
        }
        jsonReader.endArray();
    }

    private static void require(final JsonReader jsonReader, final String expectedName)
            throws IOException {
        final String actualName = jsonReader.nextName();
        if ( !actualName.equals(expectedName) ) {
            throw new JsonParseException("Expected " + expectedName + " but was " + actualName);
        }
    }

}

ReadJsonStreamIntoListTest.java

这个很像ReadAsWholeListTest但它使用简化的反序列化机制。

final class ReadJsonStreamIntoListTest
        extends AbstractJsonStreamTest {

    private static final ITest instance = new ReadJsonStreamIntoListTest();

    private ReadJsonStreamIntoListTest() {
    }

    static ITest get() {
        return instance;
    }

    @Nonnull
    @Override
    public Collection<City> test(@Nonnull final JsonReader jsonReader)
            throws IOException {
        final List<City> cities = new ArrayList<>();
        read(jsonReader, cities::add);
        cities.sort(City::compareByName);
        return cities;
    }

}

ReadJsonStreamIntoTreeSetTest.java

与前一个一样,这个也只是更昂贵的实现的另一种实现(ReadAsWholeTreeSetTest),但是它不需要自定义类型适配器。

final class ReadJsonStreamIntoTreeSetTest
        extends AbstractJsonStreamTest {

    private static final ITest instance = new ReadJsonStreamIntoTreeSetTest();

    private ReadJsonStreamIntoTreeSetTest() {
    }

    static ITest get() {
        return instance;
    }

    @Nonnull
    @Override
    public Collection<City> test(@Nonnull final JsonReader jsonReader)
            throws IOException {
        final Collection<City> cities = new TreeSet<>(City::compareByName);
        read(jsonReader, cities::add);
        return cities;
    }

}

ReadJsonStreamIntoListChunksTest.java

以下测试基于您最初的想法,但它不会并行对块进行排序(我不确定,但您可以尝试一下)。 我仍然认为前两个更简单,可能更容易维护,并且可以提供更多的性能增益。

final class ReadJsonStreamIntoListChunksTest
        extends AbstractJsonStreamTest {

    private static final ITest instance = new ReadJsonStreamIntoListChunksTest();

    private ReadJsonStreamIntoListChunksTest() {
    }

    static ITest get() {
        return instance;
    }

    @Nonnull
    @Override
    public List<City> test(@Nonnull final JsonReader jsonReader)
            throws IOException {
        final Collection<List<City>> cityChunks = new ArrayList<>();
        final AtomicReference<List<City>> cityChunkRef = new AtomicReference<>(new ArrayList<>());
        read(jsonReader, city -> {
            final List<City> cityChunk = cityChunkRef.get();
            cityChunk.add(city);
            if ( cityChunk.size() >= 10000 ) {
                cityChunks.add(cityChunk);
                cityChunkRef.set(new ArrayList<>());
            }
        });
        if ( !cityChunkRef.get().isEmpty() ) {
            cityChunks.add(cityChunkRef.get());
        }
        for ( final List<City> cities : cityChunks ) {
            Collections.sort(cities, City::compareByName);
        }
        return merge(cityChunks, City::compareByName);
    }

    /**
     * <p>Adapted from:</p>
     * <ul>
     * <li>Original question: https://stackoverflow.com/questions/1774256/java-code-review-merge-sorted-lists-into-a-single-sorted-list</li>
     * <li>Accepted answer: https://stackoverflow.com/questions/1774256/java-code-review-merge-sorted-lists-into-a-single-sorted-list/1775748#1775748</li>
     * </ul>
     */
    @SuppressWarnings("MethodCallInLoopCondition")
    private static <E> List<E> merge(final Iterable<? extends List<E>> lists, final Comparator<? super E> comparator) {
        int totalSize = 0;
        for ( final List<E> l : lists ) {
            totalSize += l.size();
        }
        final List<E> result = new ArrayList<>(totalSize);
        while ( result.size() < totalSize ) { // while we still have something to add
            List<E> lowest = null;
            for ( final List<E> l : lists ) {
                if ( !l.isEmpty() ) {
                    if ( lowest == null || comparator.compare(l.get(0), lowest.get(0)) <= 0 ) {
                        lowest = l;
                    }
                }
            }
            assert lowest != null;
            result.add(lowest.get(0));
            lowest.remove(0);
        }
        return result;
    }

}

检测结果

For my desktopJRE我可以得到以下测试结果:

                          FirstTest : in 5797 ms with 209557 elements
                ReadAsWholeListTest : in 796 ms with 209557 elements
             ReadAsWholeTreeSetTest : in 733 ms with 162006 elements
         ReadJsonStreamIntoListTest : in 461 ms with 209557 elements
      ReadJsonStreamIntoTreeSetTest : in 452 ms with 162006 elements
   ReadJsonStreamIntoListChunksTest : in 607 ms with 209557 elements
--------------------
                          FirstTest : in 3396 ms with 209557 elements
                ReadAsWholeListTest : in 493 ms with 209557 elements
             ReadAsWholeTreeSetTest : in 520 ms with 162006 elements
         ReadJsonStreamIntoListTest : in 385 ms with 209557 elements
      ReadJsonStreamIntoTreeSetTest : in 377 ms with 162006 elements
   ReadJsonStreamIntoListChunksTest : in 540 ms with 209557 elements
--------------------
                          FirstTest : in 3448 ms with 209557 elements
                ReadAsWholeListTest : in 429 ms with 209557 elements
             ReadAsWholeTreeSetTest : in 421 ms with 162006 elements
         ReadJsonStreamIntoListTest : in 400 ms with 209557 elements
      ReadJsonStreamIntoTreeSetTest : in 385 ms with 162006 elements
   ReadJsonStreamIntoListChunksTest : in 480 ms with 209557 elements
--------------------

正如你所看到的,创建了过多的Gson实例绝对是一个错误的想法。 更优化的测试可以获得更好的性能。 然而,在我的环境中,将大列表拆分为排序块(非并行)以便稍后合并并不会带来太多性能提升。

为了简单起见,可能是最好的选择,我会选择ReadJsonStreamInto_Collection_Test取决于所需的集合。 我不太确定它在真正的 Android 环境中工作得有多好,但你可以简单地进行一些 JSON 反序列化,比 Gson 使用其内部结构做得更好一些。

顺便一提:

  • 我不太确定,但是你注意到 162006 个独特的城市了吗?您的 JSON 文件probably有一些重复项(至少如果它_id是身份)。
  • 如果您只是生成一个排序版本怎么办cities.json 提前在 Android 设备上使用之前先在您的工作站上安装?此外,如果我的上述假设是正确的,您可能想要过滤掉重复项。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将大型 JSON 文件分割成块并使用 GSON 进行排序 的相关文章

随机推荐

  • 强制标准输出编码为 UTF8

    我希望从我的 C 项目中另一个应用程序的标准输出流中解析 UTF8 字符 使用默认方法 从进程的标准输出流读取时 ANSI 范围之外的字符会被损坏 现在根据微软的说法 我需要做的是设置StandardOutputEncoding 如果 St
  • 将 hibernate 添加到 netbeans 中的现有 Web 应用程序中?

    我在 netbeans 中创建了一个 Web 应用程序 它运行良好 现在出于学习目的 我想使用 hibernate 进行数据库交互 我可以在 netbeans 中使用 hibernate 创建一个新项目 但我不知道如何在现有项目中添加 hi
  • Javascript getImageData for canvas html5

    我已经把头发扯下来了 我得到了这个工作 认为 我可以承担不保存这个版本的费用 然后我 打破了 构建 线路myImageData context getImageData 0 0 canvas width canvas height 似乎打破
  • PHP进程间通信监控消息队列

    我正在开发一个项目 为排行榜和其他几个位置生成统计数据 我的目标是尽可能实时 因此我实现了一个用于消息队列的 RabbitMQ 服务器 在前端 我有一个 Nodejs 设置 用于将信息提供给正在观看它的客户 在同一台服务器上 我有一个 PH
  • InvalidOperationException:内容类型不正确:ASP.NET Core

    我有两种表格 一种用于登录 一种用于注册 它们都在同一视图上并使用同一模型 我正在使用控制器处理表单提交 我在访问登录页面时收到以下错误 InvalidOperationException Incorrect Content Type 完整
  • Custom.css 在 32.0.1700.76 m Google Chrome 更新中停止工作

    我使用此网站上的 Google 开发者工具的一些主题 http devthemez com themes chrome developer tools http devthemez com themes chrome developer t
  • 如何将元素数组转换为 NodeList?

    首先第一件事是 这不是问如何将 NodeList 转换为数组 这是相反的 为了保持一致性 我想创建一个返回 NodeList 的函数 就像document querySelectorAll does 这是我当前的代码 var toNodeL
  • 如何使用reason-react有条件地在JSX中设置HTML属性?

    我想渲染一个 HTML 复选框 其选中状态由数据控制 给出一个接收一个无状态组件item type label string checked bool Like so let component ReasonReact statelessC
  • 删除 MySQL 表中的重复条目[重复]

    这个问题在这里已经有答案了 我有一个有几千行的表 该表包含两列 name and email 我有几个重复的行 例如 约翰 史密斯 电子邮件受保护 cdn cgi l email protection 约翰 史密斯 电子邮件受保护 cdn
  • 有没有办法影响 AlchemyAPI 情感分析

    我使用 AlchemyAPI 进行文本分析 我想知道是否有办法影响 API 结果或根据要求对其进行微调 我试图分析互联网上可用的不同呼叫中心对话 了解情绪 即客户是否不满意 生气 因此对话是否消极 对于十分之九的对话 它给出的情绪是积极的
  • 无法解析:com.google.code.gson:gson.2.2.2

    我将项目从 eclipse 导入到 android studio 并在将项目与 gradle 文件同步后收到上述消息 我是 android studio 的新手 不知道如何解决这个错误 以下是 Gradle 文件 apply plugin
  • Bitbucket 上的 Git:始终要求输入密码,即使在上传我的公共 SSH 密钥后也是如此

    我上传了我的 ssh id rsa pub to Bitbucket 的 SSH 密钥 https bitbucket org account ssh keys as 解释了 https confluence atlassian com b
  • Windows Phone 8.1 设备 (Lumia 930) 和 VS2013 调试仅适用于禁用 WiFi

    当 Windows Phone 8 1 应用程序启动进行部署和调试时 VS2013 中出现错误 错误 1 错误 DEP6100 发生以下意外错误 在 boostrapping 阶段 连接到设备 SmartDeviceException 部署
  • Flutter 自动完成功能在 VSCode 中不适用于包

    我将整个 flutter 项目移动到一个新文件夹 一切运行正常 自动完成功能适用于 flutter 中的构建文件 例如 Container Column 但是当我尝试使用包 例如提供者 时 我需要进入 pub dev 页面并复制导入 手动导
  • word2vec - 什么是最好的?添加、连接或平均词向量?

    我正在研究循环语言模型 为了学习可用于初始化语言模型的词嵌入 我使用 gensim 的 word2vec 模型 训练后 word2vec 模型为词汇表中的每个单词保存两个向量 单词嵌入 输入 隐藏矩阵的行 和上下文嵌入 隐藏 输出矩阵的列
  • Hadoop集群节点数

    我正在尝试设置 Hadoop 多节点集群 当我启动集群时 这是我在控制台中的响应 hduser hadoop master usr local hadoop usr local hadoop sbin start dfs sh Starti
  • Base64 图像上传到 S3 时损坏

    router post image multipartMiddleware function req res var file name req body name var data req body data return s3fsImp
  • 如何验证空手道 BDD 响应中收到的日期

    我的响应中有两个字段 响应中有两个参数 日期1 2018年12月18日 日期2 2018年11月23日 我想测试 id date1 是否小于今天的日期 并且 date2 小于请求参数中的某个其他日期 我不知道如何在空手道模式验证中执行此操作
  • 浮动操作按钮阻止其他组件

    谷歌的新材料设计建议使用浮动操作按钮来吸引用户注意屏幕上的主要操作 列表视图中有很多这样的例子 现在 假设您的列表视图有足够的项目来填满屏幕 从而无法滚动 如果您的列表项具有用户可以与之交互的某个组件 例如开关或最喜欢的星星 则浮动操作按钮
  • 如何将大型 JSON 文件分割成块并使用 GSON 进行排序

    我有一个巨大的 JSON 文件 名为 Something json 文件大小为 20 MB 我正在用 GSON 读这篇文章 它可以在标准 Android Nexus 5X 上读取 Json 示例 country UA name Hurzuf