Java Thay thế nhiều chuỗi con khác nhau trong một chuỗi cùng một lúc (hoặc theo cách hiệu quả nhất)


97

Tôi cần thay thế nhiều chuỗi con khác nhau trong một chuỗi sao cho hiệu quả nhất. có cách nào khác sau đó là cách brute force để thay thế từng trường bằng cách sử dụng string.replace không?

Câu trả lời:


102

Nếu chuỗi bạn đang thao tác rất dài hoặc bạn đang thao tác trên nhiều chuỗi, thì việc sử dụng java.util.regex.Matcher có thể đáng giá (điều này đòi hỏi thời gian báo trước để biên dịch, vì vậy nó sẽ không hiệu quả nếu đầu vào của bạn rất nhỏ hoặc mẫu tìm kiếm của bạn thay đổi thường xuyên).

Dưới đây là một ví dụ đầy đủ, dựa trên danh sách các mã thông báo được lấy từ bản đồ. (Sử dụng StringUtils từ Apache Commons Lang).

Map<String,String> tokens = new HashMap<String,String>();
tokens.put("cat", "Garfield");
tokens.put("beverage", "coffee");

String template = "%cat% really needs some %beverage%.";

// Create pattern of the format "%(cat|beverage)%"
String patternString = "%(" + StringUtils.join(tokens.keySet(), "|") + ")%";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(template);

StringBuffer sb = new StringBuffer();
while(matcher.find()) {
    matcher.appendReplacement(sb, tokens.get(matcher.group(1)));
}
matcher.appendTail(sb);

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

Khi biểu thức chính quy được biên dịch, việc quét chuỗi đầu vào thường rất nhanh chóng (mặc dù nếu biểu thức chính quy của bạn phức tạp hoặc liên quan đến việc bẻ khóa ngược thì bạn vẫn cần phải chuẩn để xác nhận điều này!)


1
Có, cần phải được làm chuẩn cho số lần lặp lại.
techzen

5
Tôi nghĩ bạn nên thoát các ký tự đặc biệt trong mỗi mã thông báo trước khi thực hiện"%(" + StringUtils.join(tokens.keySet(), "|") + ")%";
Nhà phát triển Marius Žilėnas

Lưu ý rằng người ta có thể sử dụng StringBuilder để có tốc độ cao hơn một chút. StringBuilder không được đồng bộ hóa. chỉnh sửa Lỗi chính chỉ làm việc với java 9 mặc dù
Tinus Tate

3
Trình đọc tương lai: đối với regex, "(" và ")" sẽ bao quanh nhóm để tìm kiếm. "%" Được tính là một chữ trong văn bản. Nếu các điều khoản của bạn không bắt đầu VÀ kết thúc bằng "%", chúng sẽ không được tìm thấy. Vì vậy, hãy điều chỉnh tiền tố và hậu tố trên cả hai phần (văn bản + mã).
linuxunil

66

Thuật toán

Một trong những cách hiệu quả nhất để thay thế các chuỗi phù hợp (không có biểu thức chính quy) là sử dụng thuật toán Aho-Corasick với một Trie hiệu quả (phát âm là "try"), thuật toán băm nhanh và triển khai tập hợp hiệu quả .

Mã đơn giản

Một giải pháp đơn giản thúc đẩy Apache StringUtils.replaceEachnhư sau:

  private String testStringUtils(
    final String text, final Map<String, String> definitions ) {
    final String[] keys = keys( definitions );
    final String[] values = values( definitions );

    return StringUtils.replaceEach( text, keys, values );
  }

Điều này làm chậm các văn bản lớn.

Mã nhanh

Việc thực hiện thuật toán Aho-Corasick của Bor giới thiệu độ phức tạp hơn một chút, trở thành chi tiết triển khai bằng cách sử dụng mặt tiền có cùng chữ ký phương thức:

  private String testBorAhoCorasick(
    final String text, final Map<String, String> definitions ) {
    // Create a buffer sufficiently large that re-allocations are minimized.
    final StringBuilder sb = new StringBuilder( text.length() << 1 );

    final TrieBuilder builder = Trie.builder();
    builder.onlyWholeWords();
    builder.removeOverlaps();

    final String[] keys = keys( definitions );

    for( final String key : keys ) {
      builder.addKeyword( key );
    }

    final Trie trie = builder.build();
    final Collection<Emit> emits = trie.parseText( text );

    int prevIndex = 0;

    for( final Emit emit : emits ) {
      final int matchIndex = emit.getStart();

      sb.append( text.substring( prevIndex, matchIndex ) );
      sb.append( definitions.get( emit.getKeyword() ) );
      prevIndex = emit.getEnd() + 1;
    }

    // Add the remainder of the string (contains no more matches).
    sb.append( text.substring( prevIndex ) );

    return sb.toString();
  }

Điểm chuẩn

Đối với các điểm chuẩn, bộ đệm được tạo bằng randomNumeric như sau:

  private final static int TEXT_SIZE = 1000;
  private final static int MATCHES_DIVISOR = 10;

  private final static StringBuilder SOURCE
    = new StringBuilder( randomNumeric( TEXT_SIZE ) );

Nơi MATCHES_DIVISORquy định số lượng biến để đưa vào:

  private void injectVariables( final Map<String, String> definitions ) {
    for( int i = (SOURCE.length() / MATCHES_DIVISOR) + 1; i > 0; i-- ) {
      final int r = current().nextInt( 1, SOURCE.length() );
      SOURCE.insert( r, randomKey( definitions ) );
    }
  }

Bản thân mã điểm chuẩn ( JMH dường như quá mức cần thiết):

long duration = System.nanoTime();
final String result = testBorAhoCorasick( text, definitions );
duration = System.nanoTime() - duration;
System.out.println( elapsed( duration ) );

1.000.000: 1.000

Một điểm chuẩn vi mô đơn giản với 1.000.000 ký tự và 1.000 chuỗi được đặt ngẫu nhiên để thay thế.

  • testStringUtils: 25 giây, 25533 mili
  • testBorAhoCorasick: 0 giây, 68 mili

Không có cuộc thi.

10.000: 1.000

Sử dụng 10.000 ký tự và 1.000 chuỗi phù hợp để thay thế:

  • testStringUtils: 1 giây, 1402 mili
  • testBorAhoCorasick: 0 giây, 37 mili

Sự phân chia kết thúc.

1.000: 10

Sử dụng 1.000 ký tự và 10 chuỗi phù hợp để thay thế:

  • testStringUtils: 0 giây, 7 mili
  • testBorAhoCorasick: 0 giây, 19 mili

Đối với các dây ngắn, chi phí thiết lập Aho-Corasick làm lu mờ cách tiếp cận brute-force StringUtils.replaceEach.

Có thể sử dụng phương pháp kết hợp dựa trên độ dài văn bản để đạt được hiệu quả tốt nhất của cả hai cách triển khai.

Triển khai

Cân nhắc so sánh các cách triển khai khác cho văn bản dài hơn 1 MB, bao gồm:

Giấy tờ

Giấy tờ và thông tin liên quan đến thuật toán:


5
Kudos vì đã cập nhật câu hỏi này với thông tin mới có giá trị, điều đó thật tuyệt. Tôi nghĩ rằng điểm chuẩn JMH vẫn phù hợp, ít nhất là đối với các giá trị hợp lý như 10.000: 1.000 và 1.000: 10 (đôi khi JIT có thể thực hiện tối ưu hóa kỳ diệu).
Tunaki

loại bỏ builder.onlyWholeWords () và nó sẽ hoạt động tương tự như thay thế chuỗi.
Ondrej Sotolar

Cảm ơn bạn rất nhiều vì câu trả lời xuất sắc này. Điều này chắc chắn là rất hữu ích! Tôi chỉ muốn nhận xét rằng để so sánh hai cách tiếp cận và cũng để đưa ra một ví dụ có ý nghĩa hơn, người ta nên xây dựng Trie chỉ một lần trong cách tiếp cận thứ hai và áp dụng nó cho nhiều chuỗi đầu vào khác nhau. Tôi nghĩ đây là lợi thế chính khi có quyền truy cập vào Trie so với StringUtils: bạn chỉ xây dựng nó một lần. Tuy nhiên, cảm ơn bạn rất nhiều vì câu trả lời này. Nó chia sẻ rất tốt phương pháp luận để thực hiện cách tiếp cận thứ hai
Vic Seedoubleyew

Một điểm tuyệt vời, @VicSeedoubleyew. Bạn muốn cập nhật câu trả lời?
Dave Jarvis

9

Điều này đã làm việc cho tôi:

String result = input.replaceAll("string1|string2|string3","replacementString");

Thí dụ:

String input = "applemangobananaarefruits";
String result = input.replaceAll("mango|are|ts","-");
System.out.println(result);

Đầu ra: táo-chuối-trái cây-


Chính xác những gì tôi cần bạn tôi :)
GOXR3PLUS

7

Nếu bạn định thay đổi một Chuỗi nhiều lần, thì việc sử dụng StringBuilder thường hiệu quả hơn (nhưng hãy đo lường hiệu suất của bạn để tìm hiểu) :

String str = "The rain in Spain falls mainly on the plain";
StringBuilder sb = new StringBuilder(str);
// do your replacing in sb - although you'll find this trickier than simply using String
String newStr = sb.toString();

Mỗi khi bạn thực hiện thay thế trên một Chuỗi, một đối tượng Chuỗi mới sẽ được tạo ra, vì Chuỗi là bất biến. StringBuilder có thể thay đổi, có nghĩa là, nó có thể được thay đổi nhiều như bạn muốn.


Tôi sợ, nó không giúp được gì. Bất cứ khi nào thay thế khác với độ dài ban đầu, bạn sẽ cần một số dịch chuyển, điều này có thể tốn kém hơn so với việc xây dựng lại chuỗi. Hay tôi đang thiếu cái gì đó?
maaartinus

4

StringBuildersẽ thực hiện thay thế hiệu quả hơn, vì bộ đệm mảng ký tự của nó có thể được chỉ định độ dài cần thiết. StringBuilderđược thiết kế cho nhiều hơn là thêm vào!

Tất nhiên câu hỏi thực sự là liệu đây có phải là một sự tối ưu hóa quá xa? JVM rất tốt trong việc xử lý việc tạo nhiều đối tượng và thu thập rác sau đó, và giống như tất cả các câu hỏi tối ưu hóa, câu hỏi đầu tiên của tôi là liệu bạn đã đo lường điều này và xác định rằng đó là một vấn đề.


2

Làm thế nào về việc sử dụng phương thức ReplaceAll () ?


4
Nhiều chuỗi con khác nhau có thể được xử lý trong một regex (/substring1|substring2|.../). Tất cả phụ thuộc vào loại thay thế OP đang cố gắng thực hiện.
Avi

4
OP đang tìm kiếm cái gì hiệu quả hơnstr.replaceAll(search1, replace1).replaceAll(search2, replace2).replaceAll(search3, replace3).replaceAll(search4, replace4)
Kip

2

Rythm một công cụ mẫu java hiện được phát hành với một tính năng mới được gọi là chế độ nội suy chuỗi cho phép bạn thực hiện một số việc như:

String result = Rythm.render("@name is inviting you", "Diana");

Trường hợp trên cho thấy bạn có thể chuyển đối số vào mẫu theo vị trí. Rythm cũng cho phép bạn chuyển các đối số theo tên:

Map<String, Object> args = new HashMap<String, Object>();
args.put("title", "Mr.");
args.put("name", "John");
String result = Rythm.render("Hello @title @name", args);

Lưu ý Rythm RẤT NHANH, nhanh hơn khoảng 2 đến 3 lần so với String.format và tốc độ, bởi vì nó biên dịch mẫu thành mã byte java, hiệu suất thời gian chạy rất gần với sự đồng nhất với StringBuilder.

Liên kết:


Đây là khả năng rất cũ có sẵn với nhiều ngôn ngữ tạo khuôn mẫu như tốc độ, thậm chí là JSP. Ngoài ra, nó không trả lời câu hỏi không yêu cầu các chuỗi tìm kiếm phải ở bất kỳ định dạng nào được xác định trước.
Angsuman Chakraborty

Thật thú vị, câu trả lời được chấp nhận cung cấp một ví dụ:, "%cat% really needs some %beverage%."; không phải %mã thông báo được phân tách đó là một định dạng được xác định trước? Điểm đầu tiên của bạn thậm chí còn buồn cười hơn, JDK cung cấp rất nhiều "khả năng cũ", một số trong số chúng bắt đầu từ những năm 90, tại sao mọi người lại bận tâm sử dụng chúng? Ý kiến và downvoting của bạn không thực hiện bất kỳ ý nghĩa thực tế
Gelin Luo

Việc giới thiệu công cụ mẫu Rythm có ích gì khi đã có nhiều công cụ mẫu tồn tại từ trước và được sử dụng rộng rãi như Velocity hoặc Freemarker để khởi động? Ngoài ra, tại sao lại giới thiệu một sản phẩm khác khi các chức năng cốt lõi của Java là quá đủ. Tôi thực sự nghi ngờ tuyên bố của bạn về hiệu suất vì Mẫu cũng có thể được biên dịch. Rất thích xem một số con số thực.
Angsuman Chakraborty

Màu xanh lá cây, Bạn đang thiếu điểm. Người hỏi muốn thay thế các chuỗi tùy ý trong khi giải pháp của bạn sẽ chỉ thay thế các chuỗi ở định dạng được xác định trước như @ trước đó. Có, ví dụ sử dụng% nhưng chỉ làm ví dụ, không phải là yếu tố giới hạn. Vì vậy, bạn trả lời không trả lời câu hỏi và do đó là điểm tiêu cực.
Angsuman Chakraborty

2

Dưới đây là câu trả lời của Todd Owen . Giải pháp đó có vấn đề là nếu các phần thay thế chứa các ký tự có ý nghĩa đặc biệt trong biểu thức chính quy, bạn có thể nhận được kết quả không mong muốn. Tôi cũng muốn có thể tùy ý thực hiện tìm kiếm không phân biệt chữ hoa chữ thường. Đây là những gì tôi nghĩ ra:

/**
 * Performs simultaneous search/replace of multiple strings. Case Sensitive!
 */
public String replaceMultiple(String target, Map<String, String> replacements) {
  return replaceMultiple(target, replacements, true);
}

/**
 * Performs simultaneous search/replace of multiple strings.
 * 
 * @param target        string to perform replacements on.
 * @param replacements  map where key represents value to search for, and value represents replacem
 * @param caseSensitive whether or not the search is case-sensitive.
 * @return replaced string
 */
public String replaceMultiple(String target, Map<String, String> replacements, boolean caseSensitive) {
  if(target == null || "".equals(target) || replacements == null || replacements.size() == 0)
    return target;

  //if we are doing case-insensitive replacements, we need to make the map case-insensitive--make a new map with all-lower-case keys
  if(!caseSensitive) {
    Map<String, String> altReplacements = new HashMap<String, String>(replacements.size());
    for(String key : replacements.keySet())
      altReplacements.put(key.toLowerCase(), replacements.get(key));

    replacements = altReplacements;
  }

  StringBuilder patternString = new StringBuilder();
  if(!caseSensitive)
    patternString.append("(?i)");

  patternString.append('(');
  boolean first = true;
  for(String key : replacements.keySet()) {
    if(first)
      first = false;
    else
      patternString.append('|');

    patternString.append(Pattern.quote(key));
  }
  patternString.append(')');

  Pattern pattern = Pattern.compile(patternString.toString());
  Matcher matcher = pattern.matcher(target);

  StringBuffer res = new StringBuffer();
  while(matcher.find()) {
    String match = matcher.group(1);
    if(!caseSensitive)
      match = match.toLowerCase();
    matcher.appendReplacement(res, replacements.get(match));
  }
  matcher.appendTail(res);

  return res.toString();
}

Đây là các trường hợp thử nghiệm đơn vị của tôi:

@Test
public void replaceMultipleTest() {
  assertNull(ExtStringUtils.replaceMultiple(null, null));
  assertNull(ExtStringUtils.replaceMultiple(null, Collections.<String, String>emptyMap()));
  assertEquals("", ExtStringUtils.replaceMultiple("", null));
  assertEquals("", ExtStringUtils.replaceMultiple("", Collections.<String, String>emptyMap()));

  assertEquals("folks, we are not sane anymore. with me, i promise you, we will burn in flames", ExtStringUtils.replaceMultiple("folks, we are not winning anymore. with me, i promise you, we will win big league", makeMap("win big league", "burn in flames", "winning", "sane")));

  assertEquals("bcaacbbcaacb", ExtStringUtils.replaceMultiple("abccbaabccba", makeMap("a", "b", "b", "c", "c", "a")));
  assertEquals("bcaCBAbcCCBb", ExtStringUtils.replaceMultiple("abcCBAabCCBa", makeMap("a", "b", "b", "c", "c", "a")));
  assertEquals("bcaacbbcaacb", ExtStringUtils.replaceMultiple("abcCBAabCCBa", makeMap("a", "b", "b", "c", "c", "a"), false));

  assertEquals("c colon  backslash temp backslash  star  dot  star ", ExtStringUtils.replaceMultiple("c:\\temp\\*.*", makeMap(".", " dot ", ":", " colon ", "\\", " backslash ", "*", " star "), false));
}

private Map<String, String> makeMap(String ... vals) {
  Map<String, String> map = new HashMap<String, String>(vals.length / 2);
  for(int i = 1; i < vals.length; i+= 2)
    map.put(vals[i-1], vals[i]);
  return map;
}

1
public String replace(String input, Map<String, String> pairs) {
  // Reverse lexic-order of keys is good enough for most cases,
  // as it puts longer words before their prefixes ("tool" before "too").
  // However, there are corner cases, which this algorithm doesn't handle
  // no matter what order of keys you choose, eg. it fails to match "edit"
  // before "bed" in "..bedit.." because "bed" appears first in the input,
  // but "edit" may be the desired longer match. Depends which you prefer.
  final Map<String, String> sorted = 
      new TreeMap<String, String>(Collections.reverseOrder());
  sorted.putAll(pairs);
  final String[] keys = sorted.keySet().toArray(new String[sorted.size()]);
  final String[] vals = sorted.values().toArray(new String[sorted.size()]);
  final int lo = 0, hi = input.length();
  final StringBuilder result = new StringBuilder();
  int s = lo;
  for (int i = s; i < hi; i++) {
    for (int p = 0; p < keys.length; p++) {
      if (input.regionMatches(i, keys[p], 0, keys[p].length())) {
        /* TODO: check for "edit", if this is "bed" in "..bedit.." case,
         * i.e. look ahead for all prioritized/longer keys starting within
         * the current match region; iff found, then ignore match ("bed")
         * and continue search (find "edit" later), else handle match. */
        // if (better-match-overlaps-right-ahead)
        //   continue;
        result.append(input, s, i).append(vals[p]);
        i += keys[p].length();
        s = i--;
      }
    }
  }
  if (s == lo) // no matches? no changes!
    return input;
  return result.append(input, s, hi).toString();
}

1

Kiểm tra điều này:

String.format(str,STR[])

Ví dụ:

String.format( "Put your %s where your %s is", "money", "mouth" );

0

Tóm tắt: Triển khai một lớp cho câu trả lời của Dave, để tự động chọn thuật toán hiệu quả nhất trong hai thuật toán.

Đây là cách triển khai toàn bộ, một lớp dựa trên câu trả lời xuất sắc ở trên của Dave Jarvis . Lớp tự động chọn giữa hai thuật toán được cung cấp khác nhau, để đạt hiệu quả tối đa. (Câu trả lời này dành cho những người chỉ muốn sao chép và dán nhanh chóng.)

Lớp ReplaceStrings:

package somepackage

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.ahocorasick.trie.Emit;
import org.ahocorasick.trie.Trie;
import org.ahocorasick.trie.Trie.TrieBuilder;
import org.apache.commons.lang3.StringUtils;

/**
 * ReplaceStrings, This class is used to replace multiple strings in a section of text, with high
 * time efficiency. The chosen algorithms were adapted from: https://stackoverflow.com/a/40836618
 */
public final class ReplaceStrings {

    /**
     * replace, This replaces multiple strings in a section of text, according to the supplied
     * search and replace definitions. For maximum efficiency, this will automatically choose
     * between two possible replacement algorithms.
     *
     * Performance note: If it is known in advance that the source text is long, then this method
     * signature has a very small additional performance advantage over the other method signature.
     * (Although either method signature will still choose the best algorithm.)
     */
    public static String replace(
        final String sourceText, final Map<String, String> searchReplaceDefinitions) {
        final boolean useLongAlgorithm
            = (sourceText.length() > 1000 || searchReplaceDefinitions.size() > 25);
        if (useLongAlgorithm) {
            // No parameter adaptations are needed for the long algorithm.
            return replaceUsing_AhoCorasickAlgorithm(sourceText, searchReplaceDefinitions);
        } else {
            // Create search and replace arrays, which are needed by the short algorithm.
            final ArrayList<String> searchList = new ArrayList<>();
            final ArrayList<String> replaceList = new ArrayList<>();
            final Set<Map.Entry<String, String>> allEntries = searchReplaceDefinitions.entrySet();
            for (Map.Entry<String, String> entry : allEntries) {
                searchList.add(entry.getKey());
                replaceList.add(entry.getValue());
            }
            return replaceUsing_StringUtilsAlgorithm(sourceText, searchList, replaceList);
        }
    }

    /**
     * replace, This replaces multiple strings in a section of text, according to the supplied
     * search strings and replacement strings. For maximum efficiency, this will automatically
     * choose between two possible replacement algorithms.
     *
     * Performance note: If it is known in advance that the source text is short, then this method
     * signature has a very small additional performance advantage over the other method signature.
     * (Although either method signature will still choose the best algorithm.)
     */
    public static String replace(final String sourceText,
        final ArrayList<String> searchList, final ArrayList<String> replacementList) {
        if (searchList.size() != replacementList.size()) {
            throw new RuntimeException("ReplaceStrings.replace(), "
                + "The search list and the replacement list must be the same size.");
        }
        final boolean useLongAlgorithm = (sourceText.length() > 1000 || searchList.size() > 25);
        if (useLongAlgorithm) {
            // Create a definitions map, which is needed by the long algorithm.
            HashMap<String, String> definitions = new HashMap<>();
            final int searchListLength = searchList.size();
            for (int index = 0; index < searchListLength; ++index) {
                definitions.put(searchList.get(index), replacementList.get(index));
            }
            return replaceUsing_AhoCorasickAlgorithm(sourceText, definitions);
        } else {
            // No parameter adaptations are needed for the short algorithm.
            return replaceUsing_StringUtilsAlgorithm(sourceText, searchList, replacementList);
        }
    }

    /**
     * replaceUsing_StringUtilsAlgorithm, This is a string replacement algorithm that is most
     * efficient for sourceText under 1000 characters, and less than 25 search strings.
     */
    private static String replaceUsing_StringUtilsAlgorithm(final String sourceText,
        final ArrayList<String> searchList, final ArrayList<String> replacementList) {
        final String[] searchArray = searchList.toArray(new String[]{});
        final String[] replacementArray = replacementList.toArray(new String[]{});
        return StringUtils.replaceEach(sourceText, searchArray, replacementArray);
    }

    /**
     * replaceUsing_AhoCorasickAlgorithm, This is a string replacement algorithm that is most
     * efficient for sourceText over 1000 characters, or large lists of search strings.
     */
    private static String replaceUsing_AhoCorasickAlgorithm(final String sourceText,
        final Map<String, String> searchReplaceDefinitions) {
        // Create a buffer sufficiently large that re-allocations are minimized.
        final StringBuilder sb = new StringBuilder(sourceText.length() << 1);
        final TrieBuilder builder = Trie.builder();
        builder.onlyWholeWords();
        builder.ignoreOverlaps();
        for (final String key : searchReplaceDefinitions.keySet()) {
            builder.addKeyword(key);
        }
        final Trie trie = builder.build();
        final Collection<Emit> emits = trie.parseText(sourceText);
        int prevIndex = 0;
        for (final Emit emit : emits) {
            final int matchIndex = emit.getStart();

            sb.append(sourceText.substring(prevIndex, matchIndex));
            sb.append(searchReplaceDefinitions.get(emit.getKeyword()));
            prevIndex = emit.getEnd() + 1;
        }
        // Add the remainder of the string (contains no more matches).
        sb.append(sourceText.substring(prevIndex));
        return sb.toString();
    }

    /**
     * main, This contains some test and example code.
     */
    public static void main(String[] args) {
        String shortSource = "The quick brown fox jumped over something. ";
        StringBuilder longSourceBuilder = new StringBuilder();
        for (int i = 0; i < 50; ++i) {
            longSourceBuilder.append(shortSource);
        }
        String longSource = longSourceBuilder.toString();
        HashMap<String, String> searchReplaceMap = new HashMap<>();
        ArrayList<String> searchList = new ArrayList<>();
        ArrayList<String> replaceList = new ArrayList<>();
        searchReplaceMap.put("fox", "grasshopper");
        searchReplaceMap.put("something", "the mountain");
        searchList.add("fox");
        replaceList.add("grasshopper");
        searchList.add("something");
        replaceList.add("the mountain");
        String shortResultUsingArrays = replace(shortSource, searchList, replaceList);
        String shortResultUsingMap = replace(shortSource, searchReplaceMap);
        String longResultUsingArrays = replace(longSource, searchList, replaceList);
        String longResultUsingMap = replace(longSource, searchReplaceMap);
        System.out.println(shortResultUsingArrays);
        System.out.println("----------------------------------------------");
        System.out.println(shortResultUsingMap);
        System.out.println("----------------------------------------------");
        System.out.println(longResultUsingArrays);
        System.out.println("----------------------------------------------");
        System.out.println(longResultUsingMap);
        System.out.println("----------------------------------------------");
    }
}

Các phụ thuộc Maven cần thiết:

(Thêm những thứ này vào tệp pom của bạn nếu cần.)

    <!-- Apache Commons utilities. Super commonly used utilities.
    https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.10</version>
    </dependency>

    <!-- ahocorasick, An algorithm used for efficient searching and 
    replacing of multiple strings.
    https://mvnrepository.com/artifact/org.ahocorasick/ahocorasick -->
    <dependency>
        <groupId>org.ahocorasick</groupId>
        <artifactId>ahocorasick</artifactId>
        <version>0.4.0</version>
    </dependency>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.