Какой эффективный способ заменить много символов в строке? - PullRequest
26 голосов
/ 29 марта 2011

Обработка строк в Java - это то, что я пытаюсь научиться делать хорошо.В настоящее время я хочу взять строку и заменить любые символы, которые я найду.

Вот моя текущая неэффективная (и немного глупая IMO) функция.Это было написано, чтобы просто работать.

public String convertWord(String word)
{
    return word.toLowerCase().replace('á', 'a')
                             .replace('é', 'e')
                             .replace('í', 'i')
                             .replace('ú', 'u')
                             .replace('ý', 'y')
                             .replace('ð', 'd')
                             .replace('ó', 'o')
                             .replace('ö', 'o')
                             .replaceAll("[-]", "")
                             .replaceAll("[.]", "")
                             .replaceAll("[/]", "")
                             .replaceAll("[æ]", "ae")
                             .replaceAll("[þ]", "th");
}

Я запустил 1.000.000 прогонов, и это заняло 8182мс.Итак, как мне следует изменить эту функцию, чтобы она стала более эффективной?

Найденное решение:

Преобразование функции в эту

public String convertWord(String word)
{
    StringBuilder sb = new StringBuilder();

    char[] charArr = word.toLowerCase().toCharArray();

    for(int i = 0; i < charArr.length; i++)
    {
        // Single character case
        if(charArr[i] == 'á')
        {
            sb.append('a');
        }
        // Char to two characters
        else if(charArr[i] == 'þ')
        {
            sb.append("th");
        }
        // Remove
        else if(charArr[i] == '-')
        {
        }
        // Base case
        else
        {   
            sb.append(word.charAt(i));
        }
    }

    return sb.toString();
}

Запуск этой функции 1.000.000раз занимает 518мс.Поэтому я думаю, что это достаточно эффективно.Спасибо за помощь, ребята:)

Ответы [ 8 ]

20 голосов
/ 29 марта 2011

Вы можете создать таблицу из String [], длина которой Character.MAX_VALUE. (Включая отображение в нижний регистр)

Поскольку замены стали более сложными, время их выполнения останется неизменным.

private static final String[] REPLACEMENT = new String[Character.MAX_VALUE+1];
static {
    for(int i=Character.MIN_VALUE;i<=Character.MAX_VALUE;i++)
        REPLACEMENT[i] = Character.toString(Character.toLowerCase((char) i));
    // substitute
    REPLACEMENT['á'] =  "a";
    // remove
    REPLACEMENT['-'] =  "";
    // expand
    REPLACEMENT['æ'] = "ae";
}

public String convertWord(String word) {
    StringBuilder sb = new StringBuilder(word.length());
    for(int i=0;i<word.length();i++)
        sb.append(REPLACEMENT[word.charAt(i)]);
    return sb.toString();
} 
8 голосов
/ 29 марта 2011

Мое предложение будет следующим:

  • Преобразовать строку в массив char []
  • Выполнить через массив, проверяя каждый символ по одному (например, с помощью оператора switch)и заменяя его при необходимости
  • Преобразование массива char [] обратно в строку

Я думаю, что это, вероятно, самая высокая производительность, которую вы получите в чистой Java.

РЕДАКТИРОВАТЬ: я заметил, что вы делаете некоторые изменения, которые изменяют длину строки.В этом случае применяется тот же принцип, однако вам нужно хранить два массива и увеличивать как исходный индекс, так и целевой индекс по отдельности.Вам также может понадобиться изменить размер массива назначения, если у вас заканчивается целевое пространство (т.е. перераспределить массив большего размера и скопировать в него существующий массив назначения)

5 голосов
/ 29 марта 2011

Моя реализация основана на справочной таблице.

public static String convertWord(String str) {
    char[] words = str.toCharArray();
    char[] find = {'á','é','ú','ý','ð','ó','ö','æ','þ','-','.',
            '/'};
    String[] replace = {"a","e","u","y","d","o","o","ae","th"};
    StringBuilder out = new StringBuilder(str.length());
    for (int i = 0; i < words.length; i++) {
        boolean matchFailed = true;
        for(int w = 0; w < find.length; w++) {
            if(words[i] == find[w]) {
                if(w < replace.length) {
                    out.append(replace[w]);
                }
                matchFailed = false;
                break;
            }
        }
        if(matchFailed) out.append(words[i]);
    }
    return out.toString();
}
2 голосов
/ 29 марта 2011

Мой первый выбор - использовать StringBuilder, потому что вам нужно удалить некоторые символы из строки.

Вторым вариантом будет итерация броска массива символов и добавление обработанного символа в другой массив начального размера строки. Затем вам нужно будет скопировать массив, чтобы обрезать возможные неиспользуемые позиции.

После этого я бы сделал несколько тестов производительности, чтобы увидеть, кто лучше.

0 голосов
/ 29 марта 2011

То, что я считаю неэффективным, - это то, что вы собираетесь снова проверять символы, которые уже были заменены, что бесполезно.

Я бы получил charArray экземпляра String, повторил его и для каждого символаспам в серии if-else вот так:

char[] array = word.toCharArray();
for(int i=0; i<array.length; ++i){
    char currentChar = array[i];
    if(currentChar.equals('é'))
        array[i] = 'e';
    else if(currentChar.equals('ö'))
        array[i] = 'o';
    else if(//...
}
0 голосов
/ 29 марта 2011

Каждый раз, когда у нас возникают подобные проблемы, мы используем регулярные выражения, поскольку они являются самым быстрым способом справиться с тем, что вы пытаетесь сделать.

Вы уже пробовали регулярные выражения?

0 голосов
/ 29 марта 2011

Используйте функцию String.replaceAll.Хорошая статья, похожая на то, что вы хотите: ссылка

0 голосов
/ 29 марта 2011

Я сомневаюсь, что вы действительно можете ускорить «замену персонажа». Что касается замены регулярных выражений, вы можете заранее скомпилировать регулярные выражения

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...