Как переписать этот блок кода с помощью StringBuilder в Java? - PullRequest
3 голосов
/ 11 ноября 2008

Учитывая слово, я должен заменить некоторые конкретные алфавиты на определенные буквы, такие как 1 для a, 5 для b и т. Д. Я использую для этого регулярное выражение. Я понимаю, что StringBuilder - лучший способ справиться с этой проблемой, поскольку я делаю много строковых манипуляций. Вот что я делаю:

String word = "foobooandfoo";
String converted = "";
converted = word.replaceAll("[ao]", "1");
converted = converted.replaceAll("[df]", "2");
converted = converted.replaceAll("[n]", "3");

Моя проблема в том, как переписать эту программу с помощью StringBuilder. Я перепробовал все, но у меня ничего не получилось. Или использовать String для этого вполне нормально?

Ответы [ 9 ]

8 голосов
/ 11 ноября 2008

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

  public static void translate(StringBuilder str, char[] table)
  {
    for (int idx = 0; idx < str.length(); ++idx) {
      char ch = str.charAt(idx);
      if (ch < table.length) {
        ch = table[ch];
        str.setCharAt(idx, ch);
      }
    }
  }

Если у вас большой алфавит для ввода str или ваши отображения редки, вы можете использовать реальную карту, например:

  public static void translate(StringBuilder str, Map<Character, Character> table)
  {
    for (int idx = 0; idx < str.length(); ++idx) {
      char ch = str.charAt(idx);
      Character conversion = table.get(ch);
      if (conversion != null) 
        str.setCharAt(idx, conversion);
    }
  }

Пока эти реализации работают на месте, вы можете создать новый экземпляр StringBuilder (или добавить к экземпляру, который был передан).

2 голосов
/ 11 ноября 2008

Я бы на самом деле сказал, что код довольно хорошо работает в большинстве приложений, хотя теоретически он уступает другим методам. Если вы не хотите использовать Matcher, попробуйте это так:

StringBuilder result = new StringBuilder(word.length());

for (char c : word.toCharArray()) {
    switch (c) {
        case 'a': case 'o': result.append('1'); break;
        case 'd': case 'f': result.append('2'); break;
        case 'n': result.append('3'); break;
        default: result.append(c); break;
    }
}
1 голос
/ 13 сентября 2011

StringBuilder и StringBuffer могут иметь большую разницу в производительности в некоторых программах. Смотри: http://www.thectoblog.com/2011/01/stringbuilder-vs-stringbuffer-vs.html Который был бы веской причиной, чтобы хотеть держаться за это.

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

Тем не менее, самый простой способ сделать это - использовать строку. Но позаботиться о том, чтобы это было сделано так, чтобы минимизировать gc и другие эффекты, если производительность является проблемой.

Мне нравится подход P Arrayah, но для более общего ответа следует использовать LinkedHashMap или что-то, что сохраняет порядок на случай, если замены имеют зависимость.

Map replaceRules = new HashMap ();

Map replaceRules = new LinkedHashMap ();

1 голос
/ 11 ноября 2008

Я не верю, что ты можешь. Все API замены регулярных выражений используют String вместо StringBuilder.

Если вы в основном конвертируете каждый символ в другой, вы можете просто сделать что-то вроде:

public String convert(String text)
{
    char[] chars = new char[text.length()];
    for (int i=0; i < text.length(); i++)
    {
        char c = text.charAt(i);
        char converted;
        switch (c)
        {
            case 'a': converted = '1'; break;
            case 'o': converted = '1'; break;
            case 'd': converted = '2'; break;
            case 'f': converted = '2'; break;
            case 'n': converted = '3'; break;
            default : converted = c; break;
        }
        chars[i] = converted;
    }
    return new String(chars);
}

Однако, если вы делаете какие-либо сложные регулярные выражения, это, очевидно, не сильно поможет.

1 голос
/ 11 ноября 2008

Я не знаю, является ли StringBuilder инструментом для вас здесь. Я бы рассмотрел Matcher , который является частью пакета Java Regex и может быть быстрее, чем ваш пример выше , если вам действительно нужна производительность .

0 голосов
/ 12 ноября 2008

StringBuilder против регулярного выражения - ложная дихотомия. Причина, по которой String # replaceAll () является неправильным инструментом, заключается в том, что каждый раз, когда вы его вызываете, вы компилируете регулярное выражение и обрабатываете всю строку. Вы можете избежать всей этой лишней работы, объединив все регулярные выражения в одно и используя методы более низкого уровня в Matcher вместо replaceAll (), например:

String text = "foobooandfoo";
Pattern p = Pattern.compile("([ao])|([df])|n");
Matcher m = p.matcher(text);
StringBuffer sb = new StringBuffer();
while (m.find())
{
  m.appendReplacement(sb, "");
  sb.append(m.start(1) != -1 ? '1' :
            m.start(2) != -1 ? '2' :
                               '3');
}
m.appendTail(sb);
System.out.println(sb.toString());

Конечно, это все еще излишне; для такой простой работы я рекомендую подход Эриксона.

0 голосов
/ 11 ноября 2008

Я бы НЕ рекомендовал использовать для этого какие-либо регулярные выражения, все они на самом деле мучительно медленны, когда вы выполняете простые операции. Вместо этого я бы порекомендовал вам начать с чего-то вроде этого

// usage:
Map<String, String> replaceRules = new HashMap<String, String>();
replaceRules.put("ao", "1");
replaceRules.put("df", "2");
replaceRules.put("n", "3");
String s = replacePartsOf("foobooandfoo", replaceRules);

// actual method
public String replacePartsOf(String thisString, Map<String, String> withThese) {
    for(Entry<String, String> rule : withThese.entrySet()) {
        thisString = thisString.replaceAll(rule.getKey(), rule.getValue());
    }

    return thisString;
}

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

0 голосов
/ 11 ноября 2008

Я понимаю, что StringBuilder - лучший способ справиться с этой проблемой, так как я делаю много строковых манипуляций.

Кто тебе это сказал? Лучший способ - это тот, который более понятен для чтения, тот, который использует StringBuilder. StringBuilder - некоторые обстоятельства, но во многих он не обеспечивает ощутимого ускорения.

Не следует инициализировать «преобразованный», если значение всегда заменяется.

Вы можете удалить часть плиты котла, чтобы улучшить свой код:

String word = "foobooandfoo";
String converted = word.replaceAll("[ao]", "1")
                       .replaceAll("[df]", "2")
                       .replaceAll("[n]", "3");

Если вы хотите использовать StringBuilder, вы можете использовать этот метод

java.util.regex.Pattern # согласовань (java.lang.CharSequence)

которые принимают CharSequence (реализовано в StringBuilder). См http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#matcher(java.lang.CharSequence).

0 голосов
/ 11 ноября 2008

Я посмотрел на Matcher.replaceAll() и заметил, что он возвращает String. Поэтому я думаю, что то, что у вас есть, будет достаточно быстрым. Regex легко читается и быстро.

Помните первое правило оптимизации: не делайте этого!

...