【问题标题】:Quickest Way to Compare Two String Arrays比较两个字符串数组的最快方法
【发布时间】:2014-01-22 11:28:07
【问题描述】:

上下文

我编写了一个小型 Java 应用程序,用于对从 Oracle 到 Microsoft 的数据迁移进行基本测试。

该应用程序执行以下操作:

  • 查询 Oracle USER_TAB_COLUMNS 表以收集有关每个表及其字段的详细信息。
  • 根据收集的信息生成 SELECT 语句
  • 在 ORACLE 和 Microsoft 版本的数据库上运行 SELECT 语句,将结果保存为 Table 对象中每一行的字符串。
  • 对于每个表,比较行以发现差异
  • 为每个表输出一个文本文件,列出不匹配的行。 (用于分析)

问题

我遇到的问题是比较我拥有的两个字符串数组(Oracle 行和 Microsoft 行)。 对于某些表,可能有将近一百万行数据。虽然我当前的代码可以在几秒钟内将 1000 个 Oracle 行与 Microsoft 的行匹配 - 但时间加起来了。

目前解决问题的尝试

  • 在读入数据时而不是在比较期间连接到“行”字符串。 (之前我有自己的字符串字段,并在比较之前连接)
  • 一旦发现一行匹配,就从内部循环中中断。
  • 从循环中删除 'oracleTable.getRows().size()' 以便只执行一次此计算。

想法

  • 删除行计数器。 (这会有很大的不同吗?没有计数器就更难观察进度/速度,所以很难分辨)
  • 从列表中删除匹配的 Microsoft Row。 (我认为从 Microsoft 行列表中删除字符串是个好主意,这样同一行就不会被比较两次。我不确定这是否会增加比节省更多的处理 - 因为很难删除从列表中迭代它。

代码

        numRowsOracle = oracleTable.getRows().size();
        numRowsMicrosoft = msTable.getRows().size();

        int orRowCounter = 0;
        boolean matched;

        // Each Oracle Row
        for (String or : oracleTable.getRows()) {
            matched = false;
            orRowCounter++;

            if (orRowCounter % 1000 == 0) {
                System.out.println("Oracle Row: " + orRowCounter + " / "
                        + numRowsOracle);
            }

            // Each Microsoft Row
            for (String mr : msTable.getRows()) {
                if (mr.equalsIgnoreCase(or)) {
                    matched = true;
                    break;
                }
            }
            if (!matched) { // Adding row to list of unmatched
                unmatchedRowStrings.add(or);
            }
        }
        // Writing report on table.
        exportlogs.writeTableLog(td.getTableName(), unmatchedRowStrings
                .size(), unmatchedRowStrings, numRowsOracle,
                numRowsMicrosoft);
    }

关于如何加快速度的任何建议?我会接受不仅加快比较两个数组的想法,而且还会以不同的方式存储数据。我没有使用过其他类型的 String 存储,例如 hashmaps。不同的东西会更快吗?

【问题讨论】:

    标签: java arrays performance


    【解决方案1】:

    这是未经测试的,因此请谨慎对待,尤其是在您使用非 ascii 字符时。

    您可以一次性对数据进行小写(或大写)版本,然后使用哈希集对其进行验证。

    // make a single pass over oracle rows, so O(n)
    Set<String> oracleLower = new HashSet<>();
    for(String or : oracleTable.getRows()) {
        oracleLower.add(or.toLowerCase());
    }
    
    // make a single pass over msft rows, so O(n)
    Set<String> msftLower = new HashSet<>();
    for(String ms : microsoftTable.getRows()) {
        msftLower.add(ms.toLowerCase());
    }
    
    // make a single pass over oracle rows, again O(n)
    for(String or : oracleLower) {
        // backed by a hash table, this has a constant time lookup
        if(!msftLower.contains(or)) {
            unmatched.add(or);
        }
    }
    

    由于哈希表,每个操作都是 O(n)。不过,这需要双倍的空间要求。可能需要进行优化以仅将一个集合设为小写(可能是 msft),而仅将另一个集合(可能是 oracle)设为小写循环内 - 然后它会更像 for(String or : oracleTable.getRows()) { or = or.toLowerCase(); if(!msftLower.contains(or)) { ... } }

    【讨论】:

    • 在编写代码时,您实际上并不需要oracleLower。您可以直接使用oracleTable(如果需要,可以随时转换为小写)。
    • @Dukeling 完全正确。我开始详细说明。我只是想说明一点,从概念上讲,我们只使用数据的小写形式。此外,如果我们发现它们有用的话,使用单独的集合可以让我们利用像 retainAllremoveAll 这样的内置机制。
    • 看起来简单得不行。我会试一试。谢谢。
    • 啊哈,这太棒了。在我看我的控制台之前,'1000...2000...3000'。现在它只是直接跳到最后。非常感激! :)
    猜你喜欢
    • 2011-01-19
    • 2015-07-04
    • 2012-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多