簡體   English   中英

在此集合上執行文本替換的最有效方法是什么?

[英]What would be the most efficient way of performing text substitution on this collection?

假設您有一個List<String>集合,其中可以包含數以萬計的字符串。 如果其中某些格式為:

"This is ${0}, he likes ${1},${2} ... ${n}"

將上述字符串轉換為以下內容的最有效方式(性能方面)是什么:

"This is %1, he likes %2,%3 ... %n"

請注意,%方式從1開始。這是我的解決方案:

import java.util.regex.*;
...
String str = "I am ${0}. He is ${1}";
Pattern pat = Pattern.compile("\\\$\\{(\\d+)\\}");
Matcher mat = pat.matcher(str)
while(mat.find()) {
   str = mat.replaceFirst("%"+(Integer.parseInt(mat.group(1))+1))
   mat = pat.matcher(str);
}
System.out.println(str);

我希望它是有效的Java代碼,我現在才在GroovyConsole中編寫它。 我對更有效的解決方案感興趣,因為我認為在這么多的字符串上應用這么多的正則表達式替換可能太慢了。 最終代碼將以Java代碼而不是Groovy代碼運行,我只是使用Groovy進行快速原型開發:)

這是我的處理方式:

import java.util.regex.*;

public class Test
{
  static final Pattern PH_Pattern = Pattern.compile("\\$\\{(\\d++)\\}");

  static String changePlaceholders(String orig)
  {
    Matcher m = PH_Pattern.matcher(orig);
    if (m.find())
    {
      StringBuffer sb = new StringBuffer(orig.length());
      do {
        m.appendReplacement(sb, "");
        sb.append("%").append(Integer.parseInt(m.group(1)) + 1);
      } while (m.find());
      m.appendTail(sb);
      return sb.toString();
    }
    return orig;
  }

  public static void main (String[] args) throws Exception
  {
    String s = "I am ${0}. He is ${1}";
    System.out.printf("before: %s%nafter:  %s%n", s, changePlaceholders(s));
  }
}

在ideone.com上進行測試

appendReplacement()執行兩個主要功能:它在上一個匹配項和當前匹配項之間添加任何文本; 並解析組引用的替換字符串,並將捕獲的文本插入到它們的位置。 我們不需要第二個函數,因此我們通過為它提供一個空的替換字符串來繞過它。 然后,我們使用生成的替換文本自己調用StringBuffer的append()方法。

在Java 7中,將進一步開放該API,從而可以進行進一步的優化。 appendReplacement()功能將分解為單獨的方法,我們將能夠使用StringBuilders代替StringBuffers(在JDK 1.4中引入Pattern / Matcher時,StringBuilder尚不存在)。

但是,最有效的優化可能是編譯一次Pattern並將其保存在static final變量中。

您應該從字符串的最后一個檢查索引而不是每個迭代步驟的第一個索引開始匹配。 正如btilly在評論中提到的那樣,您的解決方案是O(n ^ 2),而應該是O(n)。 為避免不必要的字符串復制,請改用StringBuilder:

StringBuilder str = new StringBuilder("I am ${0}. He is ${1}");
Pattern pat = Pattern.compile("\\\$\\{(\\d+)\\}");
Matcher mat = pat.matcher(str);
int lastIdx = 0;
while (mat.find(lastIdx)) {
    String group = mat.group(1);
    str.replace(mat.start(1), mat.end(1), "%"+(Integer.parseInt(group)+1));
    lastIdx = mat.start(1);
}
System.out.println(str);

代碼未經測試,因此可能存在一些錯誤。

我認為使用appendReplacement會更有效,因為那樣您就不會產生大量新的String對象,並且搜索不會每次都從頭開始。

 String str = "I am ${0}. He is ${1}";
 Pattern pat = Pattern.compile("\\$\\{(\\d+)\\}");
 Matcher mat = pat.matcher(str);

 StringBuffer sb = new StringBuffer(str.length());

 while (mat.find()) {
    mat.appendReplacement(sb, "" + Integer.parseInt(mat.group(1)));
 }
 mat.appendTail(sb);

 System.out.println(sb.toString());

印刷品:

我是0。他是1

嘗試這個:

String str = "I am ${0}. He is ${1}";
Pattern pat = Pattern.compile("\\$\\{(\\d+)\\}");
Matcher mat = pat.matcher(string);
StringBuffer output = new StringBuilder(string.length());
while(mat.find()) {
   m.appendReplacement(output, "%"+(Integer.parseInt(mat.group(1))+1));
}
mat.appendTail(output);
System.out.println(output);

(主要從Javadoc復制,並從問題中進行了附加的轉換。)我認為這確實是O(n)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM