JavaRush /Blog Java /Random-VI /Chuỗi trong Java (lớp java.lang.String)
Viacheslav
Mức độ

Chuỗi trong Java (lớp java.lang.String)

Xuất bản trong nhóm

Giới thiệu

Con đường của một lập trình viên là một quá trình phức tạp và lâu dài. Và trong hầu hết các trường hợp, nó bắt đầu bằng một chương trình hiển thị Hello World trên màn hình. Java cũng không ngoại lệ (xem Bài học: Ứng dụng "Xin chào thế giới!" ). Như chúng ta có thể thấy, thông báo được xuất ra bằng cách sử dụng. System.out.println("Hello World!"); Nếu bạn nhìn vào API Java, phương thức System.out.println lấy String làm tham số đầu vào . Loại dữ liệu này sẽ được thảo luận.

Chuỗi dưới dạng một chuỗi ký tự

Thực ra String dịch từ tiếng Anh là một chuỗi. Đúng vậy, kiểu String đại diện cho một chuỗi văn bản. Chuỗi văn bản là gì? Chuỗi văn bản là một loại chuỗi ký tự được sắp xếp theo sau nhau. Biểu tượng là char. Trình tự – trình tự. Vì vậy, hoàn toàn chính xác, String là một triển khai của java.lang.CharSequence. Và nếu bạn nhìn vào bên trong lớp String, thì bên trong nó không có gì ngoài một mảng ký tự: private final char value[]; Nó có java.lang.CharSequencemột hợp đồng khá đơn giản:
Chuỗi trong Java (lớp java.lang.String) - 1
Chúng tôi có một phương thức để lấy số phần tử, lấy một phần tử cụ thể và lấy một tập hợp các phần tử + chính phương thức toString, phương thức này sẽ trả về giá trị này) Sẽ thú vị hơn khi hiểu các phương thức đến với chúng ta trong Java 8 và đây là : chars()codePoints() Nhớ lại từ Hướng dẫn về các loại dữ liệu nguyên thủy của Oracle " char đó là single 16-bit Unicode character. Nghĩa là, về cơ bản char chỉ là một loại có kích thước bằng một nửa int (32 bit) đại diện cho các số từ 0 đến 65535 (xem giá trị thập phân trong bảng ASCII ). Nghĩa là, nếu muốn, chúng ta có thể biểu diễn char dưới dạng int. Và Java 8 đã tận dụng được điều này. Bắt đầu với phiên bản 8 của Java, chúng ta có IntStream - một luồng để làm việc với các số nguyên nguyên thủy. Do đó, trong charSequence có thể nhận được IntStream đại diện cho ký tự hoặc codePoints. Trước khi chuyển sang chúng, chúng ta sẽ xem một ví dụ cho thấy sự tiện lợi của phương pháp này. Hãy sử dụng trình biên dịch java trực tuyến Tutorialspoint và thực thi mã:
public static void main(String []args){
        String line = "aaabccdddc";
        System.out.println( line.chars().distinct().count() );
}
Bây giờ bạn có thể nhận được một số biểu tượng độc đáo theo cách đơn giản này.

CodePoints

Vì vậy, chúng tôi đã tìm hiểu về ký tự. Bây giờ vẫn chưa rõ đây là loại điểm mã nào. Khái niệm codePoint xuất hiện vì khi Java xuất hiện, 16 bit (nửa int) là đủ để mã hóa một ký tự. Do đó, char trong java được thể hiện ở định dạng UTF-16 (thông số kỹ thuật "Unicode 88"). Sau đó, Unicode 2.0 xuất hiện, khái niệm này là biểu diễn một ký tự dưới dạng cặp thay thế (2 ký tự). Điều này cho phép chúng tôi mở rộng phạm vi giá trị có thể có thành giá trị int. Để biết thêm chi tiết, hãy xem stackoverflow: " So sánh char với điểm mã? " UTF-16 cũng được đề cập trong JavaDoc cho Character . Ở đó, trong JavaDoc, người ta nói rằng: In this representation, supplementary characters are represented as a pair of char values, the first from the high-surrogates range, (\uD800-\uDBFF), the second from the low-surrogates range (\uDC00-\uDFFF). Khá khó (và thậm chí có thể là không thể) để tái tạo điều này trong bảng chữ cái tiêu chuẩn. Nhưng các ký hiệu không kết thúc bằng chữ cái và số. Ở Nhật Bản, họ đã nghĩ ra một thứ rất khó mã hóa thành biểu tượng cảm xúc - ngôn ngữ của chữ tượng hình và biểu tượng cảm xúc. Có một bài viết thú vị về điều này trên Wikipedia: “ Biểu tượng cảm xúc ”. Hãy cùng tìm một ví dụ về biểu tượng cảm xúc, chẳng hạn như thế này: “ Emoji Ghost ”. Như chúng ta có thể thấy, codePoint tương tự thậm chí còn được chỉ ra ở đó (giá trị = U+1F47B). Nó được chỉ định ở định dạng thập lục phân. Nếu chúng tôi chuyển đổi sang số thập phân, chúng tôi nhận được 128123. Đây là mức cho phép nhiều hơn 16 bit (tức là hơn 65535). Hãy sao chép nó:
Chuỗi trong Java (lớp java.lang.String) - 2
Thật không may, nền tảng JavaRush không hỗ trợ các ký tự như vậy trong văn bản. Do đó, trong ví dụ bên dưới, bạn sẽ cần chèn một giá trị vào String. Vì vậy, bây giờ chúng ta sẽ hiểu một bài kiểm tra đơn giản:
public static void main(String []args){
	    String emojiString = "Вставте сюда эмоджи через ctrl+v";
	    //На один emojiString приходится 2 чара (т.к. не влезает в 16 бит)
	    System.out.println(emojiString.codePoints().count()); //1
	    System.out.println(emojiString.chars().count()); //2
}
Như bạn có thể thấy, trong trường hợp này, 1 codePoint có 2 ký tự. Đây là phép thuật.

Tính cách

Như chúng ta đã thấy ở trên, Chuỗi trong Java bao gồm char. Kiểu nguyên thủy cho phép bạn lưu trữ một giá trị, nhưng trình bao bọc java.lang.Charactertrên kiểu nguyên thủy cho phép bạn thực hiện nhiều điều hữu ích với ký hiệu này. Ví dụ: chúng ta có thể chuyển đổi một chuỗi thành chữ hoa:
public static void main(String[] args) {
    String line = "организация объединённых наций";
    char[] chars = line.toCharArray();
    for (int i = 0; i < chars.length; i++) {
        if (i == 0 || chars[i - 1] == ' ') {
            chars[i] = Character.toUpperCase(chars[i]);
        }
    }
    System.out.println(new String(chars));
}
Chà, có nhiều điều thú vị khác nhau: isAlphabetic(), isLetter(), isSpaceChar(), isDigit(), isUpperCase(), isMirrored()(ví dụ: dấu ngoặc. '(' có hình ảnh phản chiếu ')').

Bể dây

Các chuỗi trong Java là bất biến, nghĩa là không đổi. Điều này cũng được chỉ ra trong JavaDoc của chính lớp java.lang.String . Thứ hai, và cũng rất quan trọng, các chuỗi có thể được chỉ định dưới dạng chữ:
String literalString = "Hello, World!";
String literalString = "Hello, World!";
Nghĩa là, bất kỳ chuỗi trích dẫn nào, như đã nêu ở trên, thực sự là một đối tượng. Và điều này đặt ra câu hỏi - nếu chúng ta sử dụng các chuỗi quá thường xuyên và chúng có thể giống nhau (ví dụ: văn bản “Lỗi” hoặc “Thành công”), thì có cách nào để đảm bảo rằng các chuỗi không được tạo mỗi lần không? Nhân tiện, chúng tôi vẫn có Maps, trong đó khóa có thể là một chuỗi. Khi đó chúng ta chắc chắn không thể có cùng một chuỗi nhưng là các đối tượng khác nhau, nếu không chúng ta sẽ không thể lấy được đối tượng từ Map. Các nhà phát triển Java đã suy nghĩ, suy nghĩ và nghĩ ra String Pool. Đây là nơi lưu trữ các chuỗi, bạn có thể gọi nó là bộ đệm chuỗi. Không phải tất cả các dòng đều kết thúc ở đó mà chỉ có những dòng được chỉ định trong mã bằng một chữ. Bạn có thể tự mình thêm một dòng vào nhóm, nhưng sẽ nói thêm về điều đó sau. Vì vậy, trong bộ nhớ chúng ta có bộ đệm này ở đâu đó. Một câu hỏi công bằng: hồ bơi này nằm ở đâu? Câu trả lời cho vấn đề này có thể được tìm thấy trên stackoverflow: “ Hằng số chuỗi của Java tồn tại ở đâu, vùng heap hay ngăn xếp? " Nó nằm trong bộ nhớ Heap, trong một khu vực nhóm hằng số thời gian chạy đặc biệt. Nhóm hằng số thời gian chạy được phân bổ khi một lớp hoặc giao diện được tạo bởi máy ảo từ vùng phương thức - một vùng đặc biệt trong Heap mà tất cả các luồng bên trong Máy ảo Java đều có quyền truy cập. String pool mang lại cho chúng ta những gì? Điều này có một số lợi thế:
  • Các đối tượng cùng loại sẽ không được tạo
  • So sánh bằng tham chiếu nhanh hơn so với so sánh từng ký tự thông qua bằng
Nhưng nếu chúng ta muốn đưa đối tượng đã tạo vào bộ đệm này thì sao? Sau đó, chúng ta có một phương thức đặc biệt: String.intern Phương thức này thêm một chuỗi vào String Pool. Điều đáng chú ý là đây không chỉ là một loại bộ đệm nào đó ở dạng mảng (như đối với Số nguyên). Phương thức thực tập được chỉ định là "gốc". Điều này có nghĩa là bản thân phương thức này được triển khai bằng ngôn ngữ khác (chủ yếu là C++). Trong trường hợp các phương thức Java cơ bản, nhiều cách tối ưu hóa khác có thể được áp dụng cho chúng ở cấp độ JVM. Nói chung, phép thuật sẽ xảy ra ở đây. Thật thú vị khi đọc bài viết sau đây về thực tập sinh: https://habr.com/post/79913/#comment_2345814 Và có vẻ như đó là một ý tưởng hay. Nhưng điều này sẽ ảnh hưởng đến chúng ta như thế nào? Nhưng nó thực sự sẽ có tác động)
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal");
    System.out.println(test == test2);
}
Như bạn có thể thấy, các dòng giống nhau nhưng kết quả sẽ sai. Và tất cả bởi vì == so sánh không phải theo giá trị mà bằng tham chiếu. Và đây là cách nó hoạt động:
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test == test2);
}
Chỉ cần lưu ý rằng chúng tôi vẫn sẽ tạo Chuỗi mới. Tức là, thực tập sinh sẽ trả về cho chúng ta một Chuỗi từ bộ đệm, nhưng Chuỗi ban đầu mà chúng ta tìm kiếm trong bộ đệm sẽ bị loại bỏ để dọn dẹp, bởi vì không ai khác biết về anh ta. Đây rõ ràng là một sự tiêu tốn tài nguyên không cần thiết =( Vì vậy, bạn nên luôn so sánh các chuỗi bằng cách sử dụng dấu bằng để tránh lỗi đột ngột và khó phát hiện nhất có thể.
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test.equals(test2));
}
Equals thực hiện so sánh chuỗi ký tự theo ký tự.

Nối

Như chúng ta nhớ, các dòng có thể được thêm vào. Và như chúng tôi nhớ, dây của chúng tôi là bất biến. Vậy nó hoạt động như thế nào? Đúng vậy, một dòng mới sẽ được tạo, bao gồm các ký hiệu của các đối tượng được thêm vào. Có hàng triệu phiên bản về cách thức hoạt động của phép nối cộng. Một số người nghĩ rằng sẽ có một đối tượng mới mỗi lần, những người khác nghĩ rằng sẽ có một đối tượng khác. Nhưng chỉ có một người có thể đúng. Và người đó chính là trình biên dịch javac. Hãy sử dụng dịch vụ biên dịch trực tuyến và chạy:
public class HelloWorld {

    public static void main(String[] args) {
        String helloMessage = "Hello, ";
        String target = "World";
        System.out.println(helloMessage + target);
    }

}
Bây giờ, hãy lưu cái này dưới dạng kho lưu trữ zip, giải nén nó vào một thư mục và thực thi: javap –c HelloWorld Và ở đây chúng ta tìm hiểu mọi thứ:
Chuỗi trong Java (lớp java.lang.String) - 3
Tất nhiên, trong một vòng lặp, tốt hơn là bạn nên tự mình thực hiện việc nối thông qua StringBuilder. Và không phải vì một loại phép thuật nào đó, mà là do StringBuilder được tạo trước chu kỳ và trong chính chu trình đó chỉ xảy ra việc nối thêm. Nhân tiện, có một điều thú vị khác ở đây. Có một bài viết rất hay: “ Xử lý chuỗi trong Java. Phần I: Chuỗi, StringBuffer, StringBuilder ." Rất nhiều thông tin hữu ích trong các ý kiến. Ví dụ: người ta chỉ định rằng khi ghép một chế độ xem, new StringBuilder().append()...toString()tối ưu hóa nội tại sẽ có hiệu lực, được điều chỉnh bởi tùy chọn -XX:+OptimizeStringConcat, được bật theo mặc định. nội tại - được dịch là “nội bộ”. JVM xử lý những thứ như vậy theo một cách đặc biệt, xử lý chúng dưới dạng Bản địa mà không phải trả thêm chi phí như JNI. Đọc thêm: " Các phương thức nội tại trong HotSpot VM ".

StringBuilder và StringBuffer

Như chúng ta đã thấy ở trên, StringBuilder là một công cụ rất hữu ích. Chuỗi là bất biến, tức là bất biến. Và tôi muốn gấp nó lại. Vì vậy, chúng ta được cung cấp 2 lớp để giúp chúng ta: StringBuilder và StringBuffer. Sự khác biệt chính giữa hai loại này là StringBuffer được giới thiệu trong JDK1.0, trong khi StringBuilder có trong java 1.5 dưới dạng phiên bản không được đồng bộ hóa của StringBuffer để loại bỏ chi phí gia tăng của việc đồng bộ hóa phương thức không cần thiết. Cả hai lớp này đều là sự triển khai của lớp trừu tượng abstractStringBuilder - Một chuỗi ký tự có thể thay đổi. Một mảng charm được lưu trữ bên trong, được mở rộng theo quy tắc: value.length * 2 + 2. Theo mặc định, kích thước (dung lượng) của StringBuilder là 16.

Có thể so sánh

Các chuỗi có thể so sánh được, tức là thực hiện phương thức so sánh. Điều này được thực hiện bằng cách so sánh từng ký tự. Điều thú vị là độ dài tối thiểu được chọn từ hai chuỗi và một vòng lặp được thực hiện trên đó. Do đó, CompareTo sẽ trả về chênh lệch giữa các giá trị int của các ký tự chưa khớp đầu tiên cho đến độ dài chuỗi nhỏ nhất hoặc trả về chênh lệch giữa các độ dài chuỗi nếu tất cả các ký tự khớp trong độ dài chuỗi tối thiểu. Sự so sánh này được gọi là “từ điển học”.

Làm việc với chuỗi Java

Chuỗi có nhiều phương thức hữu ích:
Chuỗi trong Java (lớp java.lang.String) - 4
Có rất nhiều nhiệm vụ để làm việc với chuỗi. Ví dụ: trên Coding Bat . Ngoài ra còn có một khóa học về Coursera: " Thuật toán trên chuỗi ".

Phần kết luận

Ngay cả một cái nhìn tổng quan ngắn gọn về lớp này cũng chiếm một lượng không gian ấn tượng. Và đó không phải là tất cả. Tôi thực sự khuyên bạn nên xem báo cáo từ JPoint 2015: Alexey Shipilev - Catechism java.lang.String
#Viacheslav
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION