JavaRush /בלוג Java /Random-HE /מחרוזות ב-Java (מחלקה java.lang.String)
Viacheslav
רָמָה

מחרוזות ב-Java (מחלקה java.lang.String)

פורסם בקבוצה

מבוא

דרכו של מתכנת היא תהליך מורכב וארוך. וברוב המקרים זה מתחיל בתוכנה שמציגה את Hello World על המסך. Java אינה יוצאת דופן (ראה שיעור: אפליקציית "שלום עולם!" ). כפי שאנו יכולים לראות, ההודעה יוצאת באמצעות System.out.println("Hello World!"); אם אתה מסתכל על Java API, שיטת System.out.println לוקחת את String כפרמטר קלט . סוג זה של נתונים יידון.

מחרוזת כרצף של תווים

למעשה, מחרוזת מתורגמת מאנגלית היא מחרוזת. זה נכון, סוג String מייצג מחרוזת טקסט. מהי מחרוזת טקסט? מחרוזת טקסט היא סוג של רצף מסודר של תווים שעוקבים זה אחר זה. הסמל הוא char. רצף – רצף. אז כן, נכון לחלוטין, String הוא יישום של java.lang.CharSequence. ואם אתה מסתכל בתוך המחלקה String עצמה, אז בתוכה אין יותר מאשר מערך של תווים: private final char value[]; יש לו java.lang.CharSequenceחוזה פשוט למדי:
מחרוזות ב-Java (מחלקה java.lang.String) - 1
יש לנו שיטה לקבל את מספר האלמנטים, לקבל אלמנט ספציפי ולקבל סט אלמנטים + שיטת toString עצמה, שתחזיר את זה) יותר מעניין להבין את השיטות שהגיעו אלינו ב-Java 8, וזהו : chars()ותזכרו codePoints() מהמדריך מ-Oracle " Primititive Data" Types " ש-char הוא single 16-bit Unicode character. כלומר, בעצם char הוא רק סוג בחצי גודל של int (32 סיביות) שמייצג מספרים מ-0 עד 65535 (ראה ערכים עשרוניים) בטבלת ASCII ). כלומר, אם נרצה, נוכל לייצג char בתור int. וג'אווה 8 ניצלה זאת. החל מגרסה 8 של Java, יש לנו IntStream - זרם לעבודה עם אינטס פרימיטיבי. לכן, ב-charSequence אפשר להשיג IntStream המייצג או תווים או codePoints. לפני שנעבור אליהם, נראה דוגמה שתראה את הנוחות שבגישה זו. בואו נשתמש ב- Tutorialspoint מהדר ג'אווה מקוון ונבצע את הקוד:
public static void main(String []args){
        String line = "aaabccdddc";
        System.out.println( line.chars().distinct().count() );
}
עכשיו אתה יכול לקבל מספר סמלים ייחודיים בצורה פשוטה זו.

נקודות קוד

אז ראינו על תווים. כעת לא ברור באיזה סוג נקודות קוד מדובר. הרעיון של codePoint הופיע מכיוון שכאשר ג'אווה הופיעה, 16 סיביות (חצי int) הספיקו כדי לקודד תו. לכן, char ב-java מיוצג בפורמט UTF-16 ("מפרט Unicode 88"). מאוחר יותר הופיע Unicode 2.0, שהרעיון שלו היה לייצג דמות כזוג פונדקאי (2 תווים). זה איפשר לנו להרחיב את טווח הערכים האפשריים לערך int. לפרטים נוספים, ראה stackoverflow: " השוואת char לנקודת קוד? " UTF-16 מוזכר גם ב-JavaDoc for Character . שם, ב-JavaDoc, נאמר כי: די 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). קשה (ואולי אפילו בלתי אפשרי) לשחזר את זה באלפבית סטנדרטי. אבל הסמלים אינם מסתיימים באותיות ומספרים. ביפן הם הגיעו למשהו כל כך קשה לקידוד כמו אימוג'י - שפת האידיאוגרמות והאמוטיקונים. יש מאמר מעניין על זה בויקיפדיה: " אימוג'י ". בוא נמצא דוגמה לאמוג'י, למשל זה: " Emoji Ghost ". כפי שאנו יכולים לראות, אותו codePoint אפילו מצוין שם (ערך = U+1F47B). זה מצוין בפורמט הקסדצימלי. אם נמיר למספר עשרוני, נקבל 128123. זה יותר מ-16 סיביות לאפשר (כלומר יותר מ-65535). בואו נעתיק את זה:
מחרוזות ב-Java (מחלקה java.lang.String) - 2
לרוע המזל, פלטפורמת JavaRush אינה תומכת בתווים כאלה בטקסט. לכן, בדוגמה למטה תצטרך להכניס ערך למחרוזת. לכן, כעת נבין מבחן פשוט:
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
}
כפי שאתה יכול לראות, במקרה זה 1 codePoint מתאים ל-2 תווים. זה הקסם.

אופי

כפי שראינו למעלה, מחרוזות ב-Java מורכבות מ-char. טיפוס פרימיטיבי מאפשר לך לאחסן ערך, אבל עטיפה java.lang.Characterעל טיפוס פרימיטיבי מאפשרת לך לעשות הרבה דברים שימושיים עם הסמל הזה. לדוגמה, אנו יכולים להמיר מחרוזת לאותיות רישיות:
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));
}
ובכן, דברים מעניינים שונים: isAlphabetic(), isLetter(), isSpaceChar(), isDigit(), isUpperCase(), isMirrored()(לדוגמה, סוגריים. '(' יש תמונת מראה ')').

בריכת מיתרים

מחרוזות בג'אווה אינן ניתנות לשינוי, כלומר קבועות. זה מצוין גם ב-JavaDoc של המחלקה java.lang.String עצמה . שנית, וגם חשובה מאוד, ניתן לציין מחרוזות כפשוטות:
String literalString = "Hello, World!";
String literalString = "Hello, World!";
כלומר, כל מחרוזת מצוטטת, כאמור לעיל, היא למעשה אובייקט. וזה מעלה את השאלה - אם אנו משתמשים במחרוזות לעתים קרובות כל כך והם יכולים להיות זהים (לדוגמה, הטקסט "שגיאה" או "בהצלחה"), האם יש דרך לוודא שהמחרוזות לא נוצרות בכל פעם? אגב, עדיין יש לנו מפות, שבהן המפתח יכול להיות מחרוזת. אז בהחלט לא יכול להיות שאותן המחרוזות יהיו אובייקטים שונים, אחרת לא נוכל לקבל את האובייקט מהמפה. מפתחי Java חשבו, חשבו והגיעו עם String Pool. זהו מקום שבו מאוחסנים מחרוזות, אתה יכול לקרוא לזה מטמון מחרוזות. לא כל השורות עצמן מגיעות לשם, אלא רק השורות המצוינות בקוד על ידי מילולית. אתה יכול להוסיף קו לבריכה בעצמך, אבל על כך בהמשך. אז בזיכרון יש לנו את המטמון הזה איפשהו. שאלה הוגנת: היכן ממוקמת הבריכה הזו? את התשובה לכך ניתן למצוא ב-stackoverflow: " היכן חיה ה-String constant pool של Java, הערימה או הערימה? " הוא ממוקם בזיכרון Heap, באזור בריכה קבוע מיוחד בזמן ריצה. מאגר הקבוע Runtime מוקצה כאשר מחלקה או ממשק נוצרת על ידי המכונה הווירטואלית מאזור השיטה - אזור מיוחד ב-Heap שלכל השרשורים בתוך ה-Java Virtual Machine יש גישה אליו. מה מעניקה לנו String pool? יש לכך מספר יתרונות:
  • לא ייווצרו אובייקטים מאותו סוג
  • השוואה לפי הפניה מהירה יותר מהשוואה בין תו-תו באמצעות שווים
אבל מה אם נרצה להכניס את האובייקט שנוצר לתוך המטמון הזה? לאחר מכן, יש לנו שיטה מיוחדת: String.intern שיטה זו מוסיפה מחרוזת ל-String Pool. ראוי לציין שזה לא רק סוג של מטמון בצורת מערך (כמו עבור מספרים שלמים). שיטת המתמחה מצוינת כ"יליד". המשמעות היא שהשיטה עצמה מיושמת בשפה אחרת (בעיקר C++). במקרה של שיטות Java בסיסיות, ניתן להחיל עליהן אופטימיזציות שונות אחרות ברמת JVM. באופן כללי, קסם יקרה כאן. מעניין לקרוא את הפוסט הבא על מתמחה: https://habr.com/post/79913/#comment_2345814 וזה נראה כמו רעיון טוב. אבל איך זה ישפיע עלינו? אבל זה באמת ישפיע)
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal");
    System.out.println(test == test2);
}
כפי שאתה יכול לראות, הקווים זהים, אבל התוצאה תהיה שקרית. והכל בגלל ש== משווה לא לפי ערך, אלא לפי הפניה. וכך זה עובד:
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test == test2);
}
רק שימו לב שאנחנו עדיין ניצור מחרוזת חדשה. כלומר, המתמחה יחזיר לנו String מהמטמון, אבל ה-String המקורי שחיפשנו במטמון ייזרק לניקוי, כי אף אחד אחר לא יודע עליו. ברור שזו צריכה מיותרת של משאבים =( לכן, תמיד כדאי להשוות מחרוזות באמצעות שווים על מנת להימנע ככל האפשר שגיאות פתאומיות וקשות לזיהוי.
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test.equals(test2));
}
Equals מבצע השוואת מחרוזת תו אחר תו.

שִׁרשׁוּר

כזכור, ניתן להוסיף שורות. וכפי שאנו זוכרים, המיתרים שלנו אינם ניתנים לשינוי. אז איך זה עובד אז? נכון, נוצרת שורה חדשה, המורכבת מסמלים של האובייקטים המתווספים. יש מיליון גרסאות כיצד פועל שרשור פלוס. יש אנשים שחושבים שבכל פעם יהיה חפץ חדש, אחרים חושבים שיהיה משהו אחר. אבל יכול להיות שרק אדם אחד צודק. והמישהו הזה הוא המהדר של javac. בואו נשתמש בשירות המהדר המקוון ונפעיל:
public class HelloWorld {

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

}
עכשיו בואו נשמור את זה כארכיון zip, נחלץ אותו לספריה ונבצע: javap –c HelloWorld והנה אנחנו מגלים הכל:
מחרוזות ב-Java (מחלקה java.lang.String) - 3
בלולאה, כמובן, עדיף לעשות את השרשור באמצעות StringBuilder בעצמך. ולא בגלל איזשהו קסם, אלא כדי שה-StringBuilder נוצר לפני המחזור, ובמחזור עצמו מתרחש רק append. אגב, יש כאן עוד דבר מעניין. יש מאמר מצוין: " עיבוד מחרוזות ב-Java. חלק א': String, StringBuffer, StringBuilder ." הרבה מידע שימושי בתגובות. לדוגמה, מצוין כי בעת שרשור תצוגה, new StringBuilder().append()...toString()אופטימיזציה פנימית פועלת, המוסדרת על ידי האפשרות -XX:+OptimizeStringConcat, המופעלת כברירת מחדל. מהותי - מתורגם כ"פנימי". ה-JVM מטפל בדברים כאלה בצורה מיוחדת, ומעבד אותם כ-Native, רק ללא עלויות נוספות של JNI. קרא עוד: " שיטות פנימיות ב-HotSpot VM ".

StringBuilder ו-StringBuffer

כפי שראינו לעיל, StringBuilder הוא כלי שימושי מאוד. מיתרים אינם ניתנים לשינוי, כלומר. בלתי ניתן לשינוי. ואני רוצה לקפל אותו. לכן, אנו מקבלים 2 שיעורים שיעזרו לנו: StringBuilder ו-StringBuffer. ההבדל העיקרי בין השניים הוא ש-StringBuffer הוצג ב-JDK1.0, בעוד StringBuilder הגיע ב-java 1.5 כגרסה לא מסונכרנת של StringBuffer כדי לבטל את התקורה המוגברת של סנכרון שיטות מיותר. שתי המחלקות הללו הן מימושים של המחלקה המופשטת AbstractStringBuilder - רצף תווים שניתן לשינוי. בפנים מאוחסן מערך של צ'ארמס, אשר מורחב לפי הכלל: value.length * 2 + 2. כברירת מחדל, הגודל (הקיבולת) של StringBuilder הוא 16.

בר השוואה

המיתרים ניתנים להשוואה, כלומר. ליישם את שיטת compareTo. זה נעשה באמצעות השוואה בין תו אחר תו. מעניין לציין שהאורך המינימלי נבחר משתי מיתרים ומבוצעת מעליו לולאה. לכן, compareTo יחזיר את ההפרש בין ערכי int של התווים הראשונים ללא התאמה עד אורך המחרוזת הקטן ביותר, או יחזיר את ההפרש בין אורכי המחרוזת אם כל התווים תואמים באורך המחרוזת המינימלי. השוואה זו נקראת "לקסיקוגרפית".

עבודה עם Java Strings

למחרוזת יש שיטות שימושיות רבות:
מחרוזות ב-Java (מחלקה java.lang.String) - 4
ישנן משימות רבות לעבודה עם מחרוזות. לדוגמה, על Coding Bat . יש גם קורס על קורסרה: " אלגוריתמים על מיתרים ".

סיכום

אפילו סקירה קצרה של מחלקה זו תופסת כמות מרשימה של מקום. וזה לא הכל. אני ממליץ בחום לצפות בדוח מ-JPoint 2015: Alexey Shipilev - Catechism java.lang.String
#ויאצ'סלב
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION