Эффективное извлечение чисел из строки в 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;
}

но дело не в этом. Я бы предпочел получить это из проверенной, поддерживаемой сторонней библиотеки. Например, разбор лонгов и правильное обращение с лонгом.MIN_VALUE немного тонкий,и я обманываю выше, разбирая Инты на длинные. И выше все еще есть проблема переполнения, если проанализированное целое число больше, чем Long.МАКСИМАЛЬНОЕ ЗНАЧЕНИЕ.

есть ли такие библиотека?

мои поиски мало что дали.

3 ответов


Не волнуйтесь слишком много об объектах, если вы не испытываете реальных проблем с производительностью. Используйте текущую JVM, есть постоянные улучшения в отношении производительности и накладных расходов памяти.

вы можете взглянуть на" ByteString " из буферов протокола Google, если вы хотите иметь общую подстроку строка:

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


вы профилировали свое приложение? Вы нашли источник проблемы?

С Strings неизменяемы, есть хороший шанс, что очень мало памяти requierd и очень мало операций выполняются для создания подстроки.

Если вы действительно не испытываете проблем с памятью, сборкой мусора и т. д. просто используйте метод 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% эффективнее во времени и предположительно в памяти, хотя и более сложный (и подверженный ошибкам). С моей точки зрения, ваш подход не окупается, хотя в вашем случае это может сделать.