簡體   English   中英

從Java中的子串中有效地解析整數

[英]Efficient parsing of integers from substrings in Java

AFAIK在標准Java庫中沒有有效的方法來解析子字符串中的整數而不實際新建包含子字符串的新字符串。

我正處於從字符串中解析數百萬個整數的情況,我並不特別想為每個子字符串創建新的字符串。 復制是我不需要的開銷。

給定一個字符串s,我想要一個像這樣的方法:

parseInteger(s, startOffset, endOffset)

語義如下:

Integer.parseInt(s.substring(startOffset, endOffset))

現在,我知道我可以像這樣合理地寫這個:

public static int parse(String s, int start, int end) {
    long result = 0;
    boolean foundMinus = false;

    while (start < end) {
        char ch = s.charAt(start);
        if (ch == ' ')
            /* ok */;
        else if (ch == '-') {
            if (foundMinus)
                throw new NumberFormatException();
            foundMinus = true;
        } else if (ch < '0' || ch > '9')
            throw new NumberFormatException();
        else
            break;
        ++start;
    }

    if (start == end)
        throw new NumberFormatException();

    while (start < end) {
        char ch = s.charAt(start);
        if (ch < '0' || ch > '9')
            break;
        result = result * 10 + (int) ch - (int) '0';
        ++start;
    }

    while (start < end) {
        char ch = s.charAt(start);
        if (ch != ' ')
            throw new NumberFormatException();
        ++start;
    }
    if (foundMinus)
        result *= -1;
    if (result < Integer.MIN_VALUE || result > Integer.MAX_VALUE)
        throw new NumberFormatException();
    return (int) result;
}

但那不是重點。 我寧願從經過測試,支持的第三方庫中獲取此信息。 例如,解析long並使用Long.MIN_VALUE正確處理有點微妙,我通過將int解析為long來欺騙。 如果解析的整數大於Long.MAX_VALUE,則上面仍然存在溢出問題。

有沒有這樣的圖書館?

我的搜索結果很少。

你有沒有想過你的應用程序? 您找到了問題的根源嗎?

由於Strings是不可變的,所以很有可能需要很少的內存,並且很少有操作來創建子字符串。

除非你真的遇到內存,垃圾收集等問題,否則只需使用substring方法。 不要為你沒有的問題尋求復雜的解決方案。

此外:如果你自己實施某些東西,你可能會損失超過你的效率。 您的代碼做了很多而且非常復雜 - 但是對於默認實現,您可能非常確定它相對較快。 並且沒有錯誤。

我無法抗拒衡量你的方法的改進:

package test;

public class TestIntParse {

    static final int MAX_NUMBERS = 10000000;
    static final int MAX_ITERATIONS = 100;

    public static void main(String[] args) {
        long timeAvoidNewStrings = 0;
        long timeCreateNewStrings = 0;

        for (int i = 0; i < MAX_ITERATIONS; i++) {
            timeAvoidNewStrings += test(true);
            timeCreateNewStrings += test(false);
        }

        System.out.println("Average time method 'AVOID new strings': " + (timeAvoidNewStrings / MAX_ITERATIONS) + " ms");
        System.out.println("Average time method 'CREATE new strings': " + (timeCreateNewStrings / MAX_ITERATIONS) + " ms");
    }

    static long test(boolean avoidStringCreation) {
        long start = System.currentTimeMillis();

        for (int i = 0; i < MAX_NUMBERS; i++) {
            String value = Integer.toString((int) Math.random() * 100000);
            int intValue = avoidStringCreation ? parse(value, 0, value.length()) : parse2(value, 0, value.length());
            String value2 = Integer.toString(intValue);
            if (!value2.equals(value)) {
                System.err.println("Error at iteration " + i + (avoidStringCreation ? " without" : " with") + " string creation: " + value + " != " + value2);
            }
        }

        return System.currentTimeMillis() - start;
    }

    public static int parse2(String s, int start, int end) {
        return Integer.valueOf(s.substring(start, end));
    }

    public static int parse(String s, int start, int end) {
        long result = 0;
        boolean foundMinus = false;

        while (start < end) {
            char ch = s.charAt(start);
            if (ch == ' ')
                /* ok */;
            else if (ch == '-') {
                if (foundMinus)
                    throw new NumberFormatException();
                foundMinus = true;
            } else if (ch < '0' || ch > '9')
                throw new NumberFormatException();
            else
                break;
            ++start;
        }

        if (start == end)
            throw new NumberFormatException();

        while (start < end) {
            char ch = s.charAt(start);
            if (ch < '0' || ch > '9')
                break;
            result = result * 10 + ch - '0';
            ++start;
        }

        while (start < end) {
            char ch = s.charAt(start);
            if (ch != ' ')
                throw new NumberFormatException();
            ++start;
        }
        if (foundMinus)
            result *= -1;
        if (result < Integer.MIN_VALUE || result > Integer.MAX_VALUE)
            throw new NumberFormatException();
        return (int) result;
    }

}

結果:

Average time method 'AVOID new strings': 432 ms
Average time method 'CREATE new strings': 500 ms

你的方法在時間上大約高出14%,據說在內存中,雖然相當復雜(並且容易出錯)。 從我的觀點來看,你的方法並沒有得到回報,盡管你的情況可能會有所回報。

如果您沒有遇到實際的性能問題,請不要過於擔心對象。 使用當前的JVM,在性能和內存開銷方面有永久性的改進。

如果您想要共享基礎字符串的子字符串,您可以查看Google協議緩沖區中的“ByteString”:

https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/ByteString#substring%28int,%20int%29

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM