Эффективный способ заменить символы в строке (Java)?

Я пишу небольшую программу на JAVA, которая:

  • принимает текст как строку
  • занимает 2 массива символов

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

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

Я объясню на примере: допустим, мой текст (String): «java is awesome!»; У меня есть 2 массива (char []): «absm» и «! @ * $».

Желаемый результат - заменить «а» на «!» , от 'b' до '@' и т. д., что означает, что в результате будет получен следующий текст:

"Java - это круто!" изменено на -> "j @ v @ i * @ w * o $ e!"

Каков наиболее эффективный способ сделать это и почему? Я подумал о зацикливании текста, но потом обнаружил, что это не так эффективно.

(Можно использовать StringBuilder / String)


person Popokoko    schedule 23.11.2011    source источник
comment
Давайте посмотрим, что вы сделали на данный момент   -  person Beau Grantham    schedule 24.11.2011
comment
Если вы хотите заменить a на !, как получить j@v@?   -  person jeha    schedule 24.11.2011


Ответы (7)


StringBuilder sb = new StringBuilder(text);
    for(int i = 0; i<text.length(); i ++)
    {
        for (int j = 0; j < firstCharArray.length;j++)
        {
            if (sb.charAt(i) == firstCharArray[j])
            {
                sb.setCharAt(i, secondCharArray[j]);
                break;
            }

        }
    }

Этот способ эффективен, потому что он использует StringBuilder для изменения символов на месте (если вы использовали Strings, вам пришлось бы каждый раз создавать новые, потому что они неизменяемы.) Также он минимизирует количество проходов, которые вам нужно сделать (1 проход через текстовая строка и n проходит через первый массив, где n = text.length ())

person aeoliant    schedule 23.11.2011
comment
Хорошо, это то, что я пытался сделать. Я только что протестировал ваш код, он отлично работает! Большое спасибо. ** Небольшой вопрос: ** этот код зацикливает каждый символ в тексте, и для каждого символа он зацикливает первый массив символов. означает, что для второго символа в тексте мы снова зациклим первый символ, я предполагаю, что другого пути нет? : S - person Popokoko; 24.11.2011
comment
Что, если бы второй символ в тексте был первым символом в массиве? - person aeoliant; 24.11.2011
comment
Я не совсем понял, что ты имел в виду? Может быть, это моя вина, что я просто прочитал то, что написал тебе в последнем посте, в любом случае не совсем ясно, что я имел в виду ... допустим, у меня есть строка с 4 длиной и двумя массивами, в то время как два из них одинакового размера, скажем, 2 длины. сначала мы повторяем 1/4 символа текста и просматриваем весь массив (x2), затем мы повторяем 2/4 символа ... и так далее, мы продолжаем проверять одни и те же параметры снова и снова, поэтому я не был уверен, насколько это эффективно .. - person Popokoko; 24.11.2011

Думаю, вы ищете StringUtils.replaceEach, по крайней мере, для справки.

person jeha    schedule 23.11.2011
comment
Кажется, это именно то, что ищет OP. - person BenCole; 24.11.2011
comment
ОП просил дееспособный. Это требует массивов строк переменной длины, а не символов, что делает его несколько неэффективным. - person Ed Staub; 24.11.2011

Насколько он вам нужен? Вы делаете это для сотен, тысяч, миллионов слов ???

Я не знаю, является ли он наиболее эффективным, но вы можете использовать метод string indexOf() для каждого из ваших возможных токенов, он сообщит вам, есть ли он там, а затем вы можете одновременно заменить этот индекс соответствующим символом из другой массив.

В коде, что-то вроде (кстати, это половина псевдокода):

for(each of first array) {
    int temp = YourString.indexOf(current array field);
    if (temp >=0) {
        replace with other array
    }
}
person dann.dev    schedule 23.11.2011

Поместите 2 массива, которые у вас есть на карте

Map<Character, Character> //or Map of Strings

где ключ - «a», «b» и т. д., а значение - это символ, который вы хотите заменить на - «@» и т. д.

Затем просто замените ключи в своей строке значениями.

person Mechkov    schedule 23.11.2011
comment
Спасибо, но этот пример слишком сложен, так как я не должен использовать этот объект. - person Popokoko; 24.11.2011
comment
Без проблем. Просто о чем подумать позже. - person Mechkov; 24.11.2011

Для таких мелких вещей поиск indexOf (), вероятно, будет быстрее, чем карта, при этом «избегая» внутреннего цикла принятого ответа. Конечно, цикл все еще существует, внутри String.indexOf (), но он, вероятно, будет оптимизирован JIT-компилятором, потому что он так интенсивно используется.

static String replaceChars(String source, String from, String to)
{
    StringBuilder dest = new StringBuilder(source);
    for ( int i = 0; i < source.length(); i++ )
    {
        int foundAt = from.indexOf(source.charAt(i));
        if ( foundAt >= 0 )
            dest.setCharAt(i,to.charAt(foundAt));
    }
    return dest.toString();
}

Обновление: Oracle / Sun JIT использует SIMD по крайней мере на некоторых процессорах для indexOf (), что делает его даже быстрее, чем можно было бы предположить.

person Ed Staub    schedule 23.11.2011
comment
Я пробовал принятое решение и ваше решение. И я обнаружил, что ваше решение работает намного быстрее (в 2 раза). Большое тебе спасибо! - person Thanh Nguyen Van; 13.09.2016

Поскольку единственный способ узнать, следует ли заменять символ, - это проверить его, вам (или любому другому утилитарному методу) необходимо прокрутить весь текст, символ за другим. Вы никогда не сможете достичь большей сложности, чем O (n) (n - количество символов в тексте).

person yair    schedule 23.11.2011

Этот служебный класс, который заменяет символ или группу символов строки. Это эквивалентно bash tr и perl tr///, то есть транслитерации.

/**
 * Utility class that replaces chars of a String, aka, transliterate.
 * 
 * It's equivalent to bash 'tr' and perl 'tr///'.
 *
 */
public class ReplaceChars {

    public static String replace(String string, String from, String to) {
        return new String(replace(string.toCharArray(), from.toCharArray(), to.toCharArray()));
    }

    public static char[] replace(char[] chars, char[] from, char[] to) {

        char[] output = chars.clone();
        for (int i = 0; i < output.length; i++) {
            for (int j = 0; j < from.length; j++) {
                if (output[i] == from[j]) {
                    output[i] = to[j];
                    break;
                }
            }
        }
        return output;
    }

    /**
     * For tests!
     */
    public static void main(String[] args) {

        // Example from: https://en.wikipedia.org/wiki/Caesar_cipher
        String string = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG";
        String from = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        String to = "XYZABCDEFGHIJKLMNOPQRSTUVW";

        System.out.println();
        System.out.println("Cesar cypher: " + string);
        System.out.println("Result:       " + ReplaceChars.replace(string, from, to));
    }
}

Это результат:

Cesar cypher: THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
Result:       QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD
person fabiolimace    schedule 25.07.2020