【问题标题】:Is it possible to get distinct values from JSONObject in java是否可以从 java 中的 JSONObject 获取不同的值
【发布时间】:2018-04-17 11:35:08
【问题描述】:

我有一个包含 500k 个对象的 json 文件,这是它的格式:

"WORKORDER": [
            { 
                "Attributes": { 
                    "SITEID": {
                        "content": "BEDFORD"
                    },
                    "WONUM": {
                        "content": "1000"
                    },
                    "WOPRIORITY": {
                        "content": 2
                    },
                    "WORKTYPE": {
                        "content": "CM"
                    }
                }
            },
            { 
                "Attributes": { 
                    "SITEID": {
                        "content": "BEDFORD"
                    },
                    "WONUM": {
                        "content": "1000-10"
                    },
                    "WORKTYPE": {
                        "content": "CM"
                    }
                }
            }

我得到这样的不同值:

for (int i = 0; i < WORKORDER.length(); i++) {  
                         JSONObject obj = WORKORDER.getJSONObject(i); 

                         JSONObject att = obj.getJSONObject("Attributes"); 

                         if( att.has(col)){ // getting col from params in the servlet

                             JSONObject column = att.getJSONObject(col);
                           Object  colval =   column.get("content");

                           if(!(list.contains(colval))) {
                              out.println( colval); 
                               list.add(colval);

                             }

但仅 5000 个对象需要很长时间!

有没有办法在不解析整个 Json 文件的情况下获取任何列的不同值,否则只解析所需的列。

【问题讨论】:

  • 您可以使用Set 代替List。 (如果list 代表List)。那将是一个“更简单”的代码。现在直接过滤 JSON...除非您可以更改 JSON 生成,否则您仍然需要读取 JSON(JSONObject 只是一个解析器)。
  • 你可以尝试转换成csv,然后获取需要的列
  • @AxelH 使用集合时会加速解析吗???
  • @PankajSinghal 但转换为 csv 也比获取数据需要时间!!
  • 是和不是。这取决于您采取的实施方式。使用 HashSet 可以做到这一点,因为它使用存储桶。目前,这是杀死你的 contains 方法,它需要为 JSON 的每个元素迭代列表。一开始没问题,但是当你已经有 1000 个项目时,这些项目将被迭代 40 万次以上。

标签: java json unique distinct


【解决方案1】:

您正在对包含 500k 个元素的 JSON 进行迭代。对于每个元素,您检查它是否先前已添加到 List 中。这意味着您的逻辑将迭代列表 500k 次。

相反,您应该使用HashSet,首先,Set 防止重复值。所以你只需要set.add(value),但最有趣的是实例具有恒定的复杂度值。由于它使用存储桶来组织值,因此不必完全迭代 Set

你可以阅读更多关于in amit answer's about How can a HashSet offer constant time add operation?

请注意,HashSet 提供了 O(1) 的摊销和平均时间性能,而不是最坏的情况。这意味着,我们可能不时遭受 O(n) 操作。 所以,当箱子太拥挤时,我们只需创建一个新的更大的数组,然后将元素复制到其中。

请注意,要使用任何Hash#### 实现,您需要确保您存储的实例正确实现hashCodeequals。你可以了解更多关于这个in the community post about What issues should be considered when overriding equals and hashCode in Java?的信息。

现在解决方法:

Set<Object> sets = new HashSet<>();
for (int i = 0; i < WORKORDER.length(); i++) {  
    // ...
    Object  colval =   column.get("content");
    if(sets.add(colval)){ //`add` return true if it wasn't present already.
        out.println( colval); 
    }
}

我保留了Object 类型,但这应该是正确的类型,至少可以确保这些实例根据需要实现了这些方法。

colval 是一个Object,它可能没有正确实现所需的方法,所以我建议你正确解析它。您应该改用column.getString("content) 或检查实例类型。


为了验证这一点,我使用了一种方法来创建假 JSON:

public static JSONObject createDummyJson(int items) {
    JSONObject json = new JSONObject();
    JSONArray orders = new JSONArray();
    json.put("order", orders);

    JSONObject attributes;
    JSONObject item;
    JSONObject order;

    Random rand = new Random();
    String[] columns = {"columnA", "columnB", "columnC", "columnD"};

    for(int i = 0; i < items; ++i) {
        order = new JSONObject();
        attributes = new JSONObject();
        order.put("Attributes", attributes);
        orders.put(order);

        for(int j = 0; j < rand.nextInt(1000) % columns.length; ++j) {
            item= new JSONObject();
            long rValue = rand.nextLong();
            item.put("content",  j%3 == 0 ? ("" + rValue ) : rValue );
            attributes.put(columns[j], item);
        }
    }
    return json;
}

然后对这两种方法进行了基本基准测试,结果如下:

public static void main(String[] args) throws Exception {
    final int jsonLength = 500_000;
    JSONObject json = createDummyJson(jsonLength);
    long start = System.currentTimeMillis();
    List<Object> list = parseJson(json);
    long end = System.currentTimeMillis();
    System.out.format("List - Run in %d ms for %d items and output %d lines%n", end-start, jsonLength, list.size());

    start = System.currentTimeMillis();
    Set<Object> set = parseJsonSet(json);
    end = System.currentTimeMillis();
    System.out.format("Set - Run in %d ms for %d items and output %d lines%n", end-start, jsonLength, set.size());
}

public static List<Object> parseJson(JSONObject json) {
    String col = "columnC";

    JSONArray array = json.getJSONArray("order");

    List<Object> list = new ArrayList<>();
    for (int i = 0; i < array.length(); i++) {
        JSONObject obj = array.getJSONObject(i);
        JSONObject att = obj.getJSONObject("Attributes");
        if (att.has(col)) { // getting col from params in the servlet
            JSONObject column = att.getJSONObject(col);
            Object colval = column.get("content");

            if (!(list.contains(colval))) {
                //System.out.println(colval);
                list.add(colval);
            }
        }
    }

    return list;
}

public static Set<Object> parseJsonSet(JSONObject json) {
    String col = "columnC";

    JSONArray array = json.getJSONArray("order");

    Set<Object> set = new HashSet<>();
    for (int i = 0; i < array.length(); i++) {
        JSONObject obj = array.getJSONObject(i);
        JSONObject att = obj.getJSONObject("Attributes");
        if (att.has(col)) { // getting col from params in the servlet
            JSONObject column = att.getJSONObject(col);
            Object colval = column.get("content");

            if (set.add(colval)) {
                //System.out.println(colval);
            }
        }
    }

    return set;
}

列表 - 在 5993 毫秒内运行 500000 个项目并输出 46971 行
设置 - 500000 个项目在 62 毫秒内运行并输出 46971 行

我什至去了一个 5M 行的 JSON(删除了永远不会结束的列表)

设置 - 在 6436 毫秒内运行 5000000 个项目并输出 468895 行

重要的是,每次新插入时删除控制台打印的行,它会减少执行时间。

【讨论】:

  • @AnasElbenney 请注意最后一部分,使用 column.get 在此逻辑中不安全,请改用 getString("content")。 (忘了你已经展示了 JSON...)
  • 但我不知道用户选择的是字符串列还是数字!
  • 抱歉太长了,(设置和列表)时间相同
  • 好的 @AnasElbenney 我将生成一个虚拟 JSON 来进行一些测试。完成后我会回复你(可能是格林威治标准时间今晚)。
  • @AnasElbenney 我用“相似”的 json 运行并验证了差异。如果它不起作用,那么这不是需要时间的Set...
猜你喜欢
  • 2017-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-22
  • 2020-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多