【问题标题】:What is an efficient way to replace many characters in a string?替换字符串中许多字符的有效方法是什么?
【发布时间】:2011-07-25 03:30:21
【问题描述】:

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 毫秒。那么我应该如何继续更改此功能以使其更高效?

找到解决方案:

将函数转换为this

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 毫秒。所以我认为这足够有效。谢谢你们的帮助:)

【问题讨论】:

标签: java optimization string


【解决方案1】:

我刚刚实现了这个替换字符串的一个字符或一组字符的实用程序类。相当于bashtr和perltr///,又名,音译。我希望它可以帮助别人!

package your.package.name;

/**
 * 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

【讨论】:

    【解决方案2】:

    您可以创建一个长度为 Character.MAX_VALUE 的 String[] 表。 (包括小写的映射)

    随着替换变得越来越复杂,执行它们的时间将保持不变。

    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();
    } 
    

    【讨论】:

    • 这是帮助我创建当前代码的解决方案。所以我接受这个。但 mikera 也提供了很多帮助。
    • 一开始看起来有点疯狂,但结果数组只有64KB,一点也不差。
    • @Kobi,这个查找速度非常快,不需要任何对象。 convertWord() 只创建一个临时对象(StringBuilder)
    • 那条评论实际上是我表达支持的奇怪方式! (我只是想在投票前检查尺寸是否合理)
    • @Kobi,大小将比使用 Map&lt;Character, String&gt; 的最大大小小得多。 ;)
    【解决方案3】:

    我的实现是基于查找表的。

    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();
    }
    

    【讨论】:

      【解决方案4】:

      我的第一选择是使用StringBuilder,因为您需要从字符串中删除一些字符。

      第二种选择是迭代抛出字符数组并将处理后的字符添加到另一个字符串初始大小的数组中。然后您需要复制数组以修剪可能未使用的位置。

      在那之后,我会做一些性能测试,看看女巫一个更好。

      【讨论】:

        【解决方案5】:

        我的建议是:

        • 将字符串转换为 char[] 数组
        • 遍历数组,逐个测试每个字符(例如,使用 switch 语句)并在需要时替换它
        • 将 char[] 数组转换回字符串

        我认为这可能是您在纯 Java 中获得的最快性能。

        编辑:我注意到您正在做一些更改字符串长度的更改。在这种情况下,同样的原则适用,但是您需要保留两个数组并分别递增源索引和目标索引。如果目标空间用完,您可能还需要调整目标数组的大小(即重新分配更大的数组并将现有目标数组复制到其中)

        【讨论】:

        • 基本上,您应该遍历字符并使用StringBuilder
        • 我会做同样的事情,如果你不介意使用更多的空间,有一个小的变化:我会使用Map&lt;Character, Character&gt;,其中键是你想要替换的字符集,并且值是相应的替换。这避免了switch 语句。
        • Kobi / MarcoS - 同意你的两种方法都更干净/优雅:-) 尽管它们也会有点慢。取决于您对“高效”的真正关心程度我猜......
        • Map&lt;Char, String&gt; 会起作用,因为我将 æ 替换为 ae 例如。
        • 使用字符作为键可能会导致为字符串中的每个字符创建一个对象。 ;)
        【解决方案6】:

        我认为效率低下的是你要再次检查已经被替换的字符,这是没用的。

        我会获取 String 实例的 charArray,对其进行迭代,然后为每个字符发送一系列 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(//...
        }
        

        【讨论】:

          【解决方案7】:

          每当我们遇到此类问题时,我们都会使用正则表达式,它们是迄今为止处理您正在尝试做的事情的最快方法。

          您是否已经尝试过正则表达式?

          【讨论】:

            【解决方案8】:

            使用函数 String.replaceAll。 不错的文章,和你想要的一样:link

            【讨论】:

              【解决方案9】:

              我怀疑,你真的可以加快“字符替换”的速度。至于正则表达式替换的情况,你可以预先编译正则表达式

              【讨论】:

                猜你喜欢
                • 2010-11-18
                • 2012-01-05
                • 2018-03-28
                • 2012-03-06
                • 1970-01-01
                • 2018-08-10
                • 2011-01-12
                • 1970-01-01
                相关资源
                最近更新 更多