JavaRush /Blog Java /Random-VI /Thay thế chuỗi trong Java

Thay thế chuỗi trong Java

Xuất bản trong nhóm
Trong công việc của một lập trình viên, một số nhiệm vụ hoặc thành phần của chúng thường được lặp lại. Vì vậy, hôm nay tôi muốn đề cập đến một chủ đề thường gặp trong công việc hàng ngày của bất kỳ nhà phát triển Java nào. Thay thế chuỗi trong Java - 1Giả sử bạn nhận được một chuỗi nhất định từ một phương thức nhất định. Và mọi thứ về nó có vẻ tốt, nhưng có một số điều nhỏ không phù hợp với bạn. Ví dụ: dấu phân cách không phù hợp và bạn cần một số dấu phân cách khác (hoặc hoàn toàn không). Có thể làm gì trong tình huống như vậy? Đương nhiên, hãy sử dụng các phương thức replacecủa lớp String.

Thay thế chuỗi Java

Đối tượng loại Stringcó bốn biến thể của phương thức thay thế replace:
  • replace(char, char);
  • replace(CharSequence, CharSequence);
  • replaceFirst(String, String);
  • replaceAll(String, String).
Mục đích của tất cả các phương pháp này là như nhau - thay thế một phần của chuỗi bằng một chuỗi khác. Chúng ta hãy xem xét kỹ hơn về họ. 1.replace(char, char) String replace(char oldChar, char newChar) - thay thế tất cả các lần xuất hiện của ký tự của đối số thứ nhất oldCharbằng đối số thứ hai - newChar. Trong ví dụ này, chúng tôi sẽ thay thế dấu phẩy bằng dấu chấm phẩy:
String value = "In JavaRush, Diego the best, Diego is Java God".replace(',', ';');
System.out.println(value);
Đầu ra của bảng điều khiển:
In JavaRush; Diego the best; Diego is Java God
2.replace(CharSequence, CharSequence) Thay thế từng chuỗi con của một chuỗi khớp với một chuỗi ký tự đã chỉ định bằng một chuỗi ký tự thay thế.
String value = "In JavaRush, Diego the best, Diego is Java God".replace("Java", "Rush");
System.out.println(value);
Phần kết luận:
In RushRush, Diego the best, Diego is Rush God
3.replaceFirst(String, String) String replaceFirst(String regex, String replacement) - Thay thế chuỗi con đầu tiên khớp với biểu thức chính quy đã chỉ định bằng chuỗi thay thế. Khi sử dụng biểu thức chính quy không hợp lệ, bạn có thể bắt gặp ngoại lệ PatternSyntaxException (đây không phải là điều tốt). Trong ví dụ này, hãy thay thế tên robot vô địch:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceFirst("Diego", "Amigo");
System.out.println(value);
Đầu ra của bảng điều khiển:
In JavaRush, Amigo the best, Diego is Java God
Như chúng ta có thể thấy, chỉ có lần xuất hiện đầu tiên của “Diego” là thay đổi, nhưng những lần tiếp theo vẫn bị bỏ qua - nghĩa là không bị ảnh hưởng. 4. replaceAll()trong Java String replaceAll(String regex, String replacement) - phương thức này thay thế tất cả các lần xuất hiện của chuỗi con trong chuỗi regexbằng replacement. Một biểu thức chính quy có thể được sử dụng làm đối số đầu tiên regex. Ví dụ: hãy thử thực hiện thay thế trước đó bằng tên, nhưng bằng một phương thức mới:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("Diego", "Amigo");
System.out.println(value);
Đầu ra của bảng điều khiển:
In JavaRush, Amigo the best, Amigo is Java God
Như chúng ta có thể thấy, tất cả các biểu tượng đã được thay thế hoàn toàn bằng những biểu tượng cần thiết. Tôi nghĩ Amigo sẽ hài lòng =)

Biểu thức chính quy

Ở trên đã nói rằng có thể thay thế bằng cách sử dụng biểu thức chính quy. Trước tiên, chúng ta hãy tự làm rõ biểu thức chính quy là gì? Biểu thức chính quy là ngôn ngữ chính thức để tìm kiếm và thao tác các chuỗi con trong văn bản, dựa trên việc sử dụng siêu ký tự (ký tự đại diện). Nói một cách đơn giản, đó là một mẫu ký tự và siêu ký tự xác định quy tắc tìm kiếm. Ví dụ: \D- một mẫu mô tả bất kỳ ký tự phi số nào; \d- xác định bất kỳ ký tự số nào, cũng có thể được mô tả là [0-9]; [a-zA-Z]- một mẫu mô tả các ký tự Latinh từ a đến z, không phân biệt chữ hoa chữ thường; Hãy xem xét ứng dụng trong một phương thức replaceAlllớp String:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("\\s[a-zA-Z]{5}\\s", " Amigo ");
System.out.println(value);
Đầu ra của bảng điều khiển:
In JavaRush, Amigo the best, Amigo is Java God
\\s[a-zA-Z]{5}\\s— mô tả một từ gồm 5 ký tự Latin được bao quanh bởi khoảng trắng. Theo đó, mẫu này được thay thế bằng chuỗi chúng ta đã truyền vào.

Thay thế biểu thức chính quy Java

Về cơ bản, để sử dụng các biểu thức chính quy trong Java, các khả năng của java.util.regex. Các lớp chính là:
  1. Pattern- một lớp cung cấp phiên bản đã biên dịch của một biểu thức chính quy.
  2. Matcher— lớp này diễn giải mẫu và xác định kết quả khớp trong chuỗi mà nó nhận được.
Thông thường, hai lớp này hoạt động cùng nhau. Vì vậy, đối tượng trước đây của chúng ta sẽ trông như thế nào, nhưng với sự trợ giúp của Matcherand Pattern:
Pattern pattern = Pattern.compile("\\s[a-zA-Z]{5}\\s");
Matcher matcher = pattern.matcher("In JavaRush, Diego the best, Diego is Java God");
String value = matcher.replaceAll(" Amigo ");
System.out.println(value);
Và kết luận của chúng tôi sẽ giống nhau:
In JavaRush, Amigo the best, Amigo is Java God
Bạn có thể đọc thêm về biểu thức chính quy trong bài viết này .

Thay thế để thay thếTất cả

Không còn nghi ngờ gì nữa, các phương pháp này replacerất Stringấn tượng, nhưng người ta không thể bỏ qua sự thật rằng Stringnó là immutablemột đối tượng, tức là nó không thể thay đổi sau khi được tạo ra. Do đó, khi chúng ta thay thế một số phần của chuỗi bằng các phương thức replace, chúng ta không thay đổi đối tượng Stringmà mỗi lần tạo một đối tượng mới với nội dung cần thiết. Nhưng mỗi lần tạo một đối tượng mới sẽ mất nhiều thời gian phải không? Đặc biệt khi câu hỏi không phải là một vài đồ vật mà là vài trăm, thậm chí hàng nghìn. Dù muốn hay không, bạn cũng bắt đầu nghĩ về những lựa chọn thay thế. Và chúng ta có những lựa chọn thay thế nào? Thay thế chuỗi trong Java - 2Hmm... Khi nói đến Stringthuộc tính của nó immutable, bạn nghĩ ngay đến các lựa chọn thay thế, nhưng không phải vậy immutable, cụ thể là StringBuilder/StringBuffer . Như chúng ta nhớ, các lớp này không thực sự khác nhau, ngoại trừ việc StringBufferchúng được tối ưu hóa để sử dụng trong môi trường đa luồng, do đó StringBuilderchúng hoạt động nhanh hơn một chút khi sử dụng đơn luồng. Dựa trên điều này, hôm nay chúng ta sẽ sử dụng StringBuilder. Lớp này có nhiều phương thức thú vị, nhưng cụ thể là bây giờ chúng ta quan tâm đến replace. StringBuilder replace(int start, int end, String str)— phương thức này thay thế các ký tự trong chuỗi con của chuỗi này bằng các ký tự trong chuỗi đã chỉ định. Chuỗi con bắt đầu ở phần đầu được chỉ định và tiếp tục cho đến ký tự ở cuối chỉ mục hoặc -1cho đến cuối chuỗi nếu không có ký tự đó tồn tại. Hãy xem một ví dụ:
StringBuilder strBuilder = new StringBuilder("Java Rush");
strBuilder.replace(5, 9, "God");
System.out.println(strBuilder);
Phần kết luận:
Java God
Như bạn có thể thấy, chúng tôi chỉ ra khoảng mà chúng tôi muốn viết chuỗi và viết một chuỗi con lên trên chuỗi trong khoảng đó. Vì vậy, bằng cách sử dụng trợ giúp, StringBuilderchúng tôi sẽ tạo lại một phương thức tương tự replaceall java. Nó sẽ trông như thế nào:
public static String customReplaceAll(String str, String oldStr, String newStr) {

   if ("".equals(str) || "".equals(oldStr) || oldStr.equals(newStr)) {
       return str;
   }
   if (newStr == null) {
       newStr = "";
   }
   final int strLength = str.length();
   final int oldStrLength = oldStr.length();
   StringBuilder builder = new StringBuilder(str);

   for (int i = 0; i < strLength; i++) {
       int index = builder.indexOf(oldStr, i);

       if (index == -1) {
           if (i == 0) {
               return str;
           }
           return builder.toString();
       }
       builder = builder.replace(index, index + oldStrLength, newStr);

   }
       return builder.toString();
}
Thoạt nhìn thì có vẻ đáng sợ nhưng chỉ cần tìm hiểu một chút bạn có thể hiểu rằng mọi thứ không quá phức tạp và khá logic. Chúng ta có ba lập luận:
  • str— một dòng mà chúng ta muốn thay thế một số chuỗi con;
  • oldStr— biểu diễn các chuỗi con mà chúng ta sẽ thay thế;
  • newStr- chúng ta sẽ thay thế nó bằng cái gì.
ifChúng ta cần cái đầu tiên để kiểm tra dữ liệu đến và nếu chuỗi strtrống oldStrhoặc chuỗi con mới newStrbằng chuỗi cũ oldStrthì việc thực thi phương thức sẽ vô nghĩa. Do đó, chúng tôi trả về chuỗi gốc - str. Tiếp theo, chúng tôi kiểm tra newStr, nullvà nếu đúng như vậy thì chúng tôi sẽ chuyển đổi nó thành định dạng chuỗi trống thuận tiện hơn cho chúng tôi - "". Sau đó chúng ta khai báo các biến chúng ta cần:
  • tổng chiều dài chuỗi str;
  • độ dài chuỗi con oldStr;
  • đối tượng StringBuildertừ một chuỗi được chia sẻ.
Chúng ta bắt đầu một vòng lặp sẽ chạy một số lần bằng độ dài của toàn bộ chuỗi (nhưng rất có thể điều này sẽ không bao giờ xảy ra). Sử dụng phương thức lớp StringBuilder- indexOf- chúng ta tìm ra chỉ mục xuất hiện đầu tiên của chuỗi con mà chúng ta quan tâm. Thật không may, tôi muốn lưu ý rằng nó indexOfkhông hoạt động với các biểu thức chính quy, vì vậy phương thức cuối cùng của chúng tôi sẽ chỉ hoạt động với các lần xuất hiện của chuỗi (( Nếu chỉ số này bằng -1, thì sẽ không còn lần xuất hiện nào của các lần xuất hiện này trong đối tượng hiện tại StringBuilder, vì vậy chúng ta thoát khỏi phương thức với kết quả quan tâm: nó được chứa trong StringBuilder, mà chúng ta chuyển đổi thành String, bằng cách sử dụng toString. Nếu chỉ số của chúng ta bằng -1trong lần lặp đầu tiên của vòng lặp, thì chuỗi con cần được thay thế không nằm trong chuỗi chung chuỗi ban đầu. Do đó, trong tình huống như vậy, chúng ta chỉ cần trả về chuỗi chung. Tiếp theo, chúng ta có và phương thức được mô tả ở trên được sử dụng replaceđể StringBuildersử dụng chỉ mục tìm thấy của lần xuất hiện để chỉ ra tọa độ của chuỗi con được thay thế. Vòng lặp này sẽ chạy tìm được bao nhiêu lần chuỗi con cần thay thế, nếu chuỗi chỉ gồm ký tự cần thay thế thì chỉ trong trường hợp này chúng ta có vòng lặp chạy hoàn toàn và chúng ta sẽ nhận được kết quả được StringBuilderchuyển đổi thành chuỗi. Chúng ta cần kiểm tra tính đúng đắn của phương pháp này phải không? Hãy viết một bài kiểm tra để kiểm tra hoạt động của phương thức trong các tình huống khác nhau:
@Test
public void customReplaceAllTest() {
   String str = "qwertyuiop__qwertyuiop__";

   String firstCase = Solution.customReplaceAll(str, "q", "a");
   String firstResult = "awertyuiop__awertyuiop__";
   assertEquals(firstCase, firstResult);

   String secondCase = Solution.customReplaceAll(str, "q", "ab");
   String secondResult = "abwertyuiop__abwertyuiop__";
   assertEquals(secondCase, secondResult);

   String thirdCase = Solution.customReplaceAll(str, "rtyu", "*");
   String thirdResult = "qwe*iop__qwe*iop__";
   assertEquals(thirdCase, thirdResult);

   String fourthCase = Solution.customReplaceAll(str, "q", "");
   String fourthResult = "wertyuiop__wertyuiop__";
   assertEquals(fourthCase, fourthResult);

   String fifthCase = Solution.customReplaceAll(str, "uio", "");
   String fifthResult = "qwertyp__qwertyp__";
   assertEquals(fifthCase, fifthResult);

   String sixthCase = Solution.customReplaceAll(str, "", "***");
   assertEquals(sixthCase, str);

   String seventhCase = Solution.customReplaceAll("", "q", "***");
   assertEquals(seventhCase, "");
}
Có thể chia thành 7 test riêng biệt, mỗi test sẽ chịu trách nhiệm cho test case riêng của mình. Sau khi khởi động nó, chúng ta sẽ thấy nó có màu xanh, tức là đã thành công. Chà, có vẻ như vậy là hết rồi. Mặc dù chờ đợi nhưng chúng tôi đã nói ở trên rằng phương pháp này sẽ nhanh hơn nhiều replaceAllso với String. Vâng, chúng ta hãy xem:
String str = "qwertyuiop__qwertyuiop__";
long firstStartTime = System.nanoTime();

for (long i = 0; i < 10000000L; i++) {
   str.replaceAll("tyu", "#");
}

double firstPerformance = System.nanoTime() - firstStartTime;

long secondStartTime = System.nanoTime();

for (long i = 0; i < 10000000L; i++) {
   customReplaceAll(str, "tyu", "#");
}

double secondPerformance = System.nanoTime() - secondStartTime;

System.out.println("Performance ratio  - " +  firstPerformance / secondPerformance);
Tiếp theo, mã này được chạy ba lần và chúng tôi nhận được kết quả như sau: Đầu ra của bảng điều khiển:
Performance ratio  - 5.012148941181627
 
Performance ratio  - 5.320637176017641
 
Performance ratio  - 4.719192686500394
Như chúng ta có thể thấy, trung bình phương pháp của chúng tôi có năng suất cao gấp 5 lần so với replaceAlllớp cổ điển ! StringChà, cuối cùng, chúng ta hãy thực hiện cùng một cuộc kiểm tra, nhưng có thể nói là vô ích. Nói cách khác, trong trường hợp không tìm thấy kết quả phù hợp. Hãy thay thế chuỗi tìm kiếm từ "tyu"thành "--". Ba lần chạy mang lại kết quả như sau: Đầu ra bảng điều khiển:
Performance ratio  - 8.789647093542246
 
Performance ratio  - 9.177105482660881
 
Performance ratio  - 8.520964375227406
Trung bình, hiệu suất đối với các trường hợp không tìm thấy kết quả phù hợp tăng 8,8 lần! Thay thế chuỗi trong Java - 4
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION