java - 使用 Java-Large 文件查询 JSON 文件

标签 java json performance

我正在尝试使用 java 解析下面的 JSON 文件。 我需要能够

  • 按 ID 或名称或对象中的任何字段搜索文件。
  • 同时搜索字段中的空值。

搜索应返回整个对象。 文件将会很大,并且搜索应该仍然很省时。


[
  {
    "id": 1,
    "name": "Mark Robb",
    "last_login": "2013-01-21T05:13:41 -11:30",
    "email": "markrobb@gmail.com",
    "phone": "12345",
    "locations": [
        "Germany",
        "Austria"
    ]
},
  {
    "id": 2,
    "name": "Matt Nish",
    "last_login": "2014-02-21T07:10:41 -11:30",
    "email": "mattnish@gmail.com",
    "phone": "456123",
    "locations": [
        "France",
        "Italy"
    ]
 }
]


这是我迄今为止使用 jackson 库尝试过的。

public void findById(int id) {
List<Customer> customers = objectMapper.readValue(new File("src/main/resources/customers.json"), new    TypeReference<List<Customer>>(){});

            for(Customer customer: customers) {
                if(customer.getId() == id) {
                    System.out.println(customer.getName());
                }
            }
}

我只是认为这对于一个巨大的 JSON 文件(一个文件中大约有 20000 个客户)来说不是一个有效的方法。并且可能有多个文件。搜索时间不应线性增加。 我怎样才能让这个时间变得高效呢?我应该使用其他库吗?

最佳答案

最有效(CPU 和内存)的解析方式是使用面向流的解析而不是对象映射。通常,需要编写更多的代码,但通常也是很划算的:) Gson 和 Jackson 都支持这种轻量级技术。另外,您应该避免在主/热路径中分配内存,以防止 GC 暂停。为了说明这个想法,我使用了一个小型的无 GC 库 https://github.com/anatolygudkov/green-jelly :

import org.green.jelly.*;    
import java.io.CharArrayReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;

public class SelectById {
    public static class Customer {
        private long id;
        private String name;
        private String email;

        public void clear() {
            id = 0;
            name = null;
            email = null;
        }

        public Customer makeCopy() {
            Customer result = new Customer();
            result.id = id;
            result.name = name;
            result.email = email;
            return result;
        }

        @Override
        public String toString() {
            return "Customer{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", email='" + email + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) throws Exception {
        final String file = "\n" +
            "[\n" +
            "  {\n" +
            "    \"id\": 1,\n" +
            "    \"name\": \"Mark Robb\",\n" +
            "    \"last_login\": \"2013-01-21T05:13:41 -11:30\",\n" +
            "    \"email\": \"markrobb@gmail.com\",\n" +
            "    \"phone\": \"12345\",\n" +
            "    \"locations\": [\n" +
            "        \"Germany\",\n" +
            "        \"Austria\"\n" +
            "    ]\n" +
            "},\n" +
            "  {\n" +
            "    \"id\": 2,\n" +
            "    \"name\": \"Matt Nish\",\n" +
            "    \"last_login\": \"2014-02-21T07:10:41 -11:30\",\n" +
            "    \"email\": \"mattnish@gmail.com\",\n" +
            "    \"phone\": \"456123\",\n" +
            "    \"locations\": [\n" +
            "        \"France\",\n" +
            "        \"Italy\"\n" +
            "    ]\n" +
            " }\n" +
            "]\n";

        final List<Customer> selection = new ArrayList<>();

        final long selectionId = 2;

        final JsonParser parser = new JsonParser().setListener(
            new JsonParserListenerAdaptor() {
                private final Customer customer = new Customer();
                private String currentField;
                @Override
                public boolean onObjectStarted() {
                    customer.clear();
                    return true;
                }

                @Override
                public boolean onObjectMember(final CharSequence name) {
                    currentField = name.toString();
                    return true;
                }

                @Override
                public boolean onStringValue(final CharSequence data) {
                    switch (currentField) {
                        case "name":
                            customer.name = data.toString();
                            break;
                        case "email":
                            customer.email = data.toString();
                            break;
                    }
                    return true;
                }

                @Override
                public boolean onNumberValue(final JsonNumber number) {
                    if ("id".equals(currentField)) {
                        customer.id = number.mantissa();
                    }
                    return true;
                }

                @Override
                public boolean onObjectEnded() {
                    if (customer.id == selectionId) {
                        selection.add(customer.makeCopy());
                        return false; // we don't need to continue
                    }
                    return true;
                }
            }
        );

        // now let's read and parse the data with a buffer

        final CharArrayCharSequence buffer = new CharArrayCharSequence(1024);

        try (final Reader reader = new CharArrayReader(file.toCharArray())) { // replace by FileReader, for example
            int len;
            while((len = reader.read(buffer.getChars())) != -1) {
                buffer.setLength(len);
                parser.parse(buffer);
            }
        }
        parser.eoj();

        System.out.println(selection);
    }
}

它在 Java 中的运行速度应该尽可能快(以防我们无法直接使用 SIMD 指令)。要完全摆脱主路径中的内存分配(和 GC 暂停),您必须将“.toString()”(它创建 String 的新实例)替换为像 StringBuilder 这样可重用的东西。

最后可能影响整体性能的是文件读取的方法。 RandomAccessFile 是 Java 中最好的选择之一。由于您的编码似乎是 ASCII,因此只需将字节转换为字符即可传递给 JsonParser。

关于java - 使用 Java-Large 文件查询 JSON 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59041703/

相关文章:

java - JClouds:使用 Google Compute Engine 自定义卷

java - 等待 vertx 中多个可观察对象的响应

javascript - 通过 JSON 中的键查找值

python - 加速 BeautifulSoup

mysql - 了解 MySQL 解释, `rows` -wise

java - 计算数据库中特定记录的数量

java - 按键聚合 hashmap 中的值

json - 当我运行 POST 请求时,为什么它不返回 Mongoose 架构定义中定义的 JSON?

iphone - 用于选择器 View iPhone 的 JSON 数组

string - 为什么 Haskell 的默认字符串实现是一个字符的链表?