JavaRush /בלוג Java /Random-HE /מחרוזת ג'אווה. שאלות ותשובות לראיון, חלק 2
Andrey
רָמָה

מחרוזת ג'אווה. שאלות ותשובות לראיון, חלק 2

פורסם בקבוצה
לרוע המזל, המאמר לא השתלב בקטע אחד, נאלצתי לפצל אותו לשני חלקים. ראה את ההתחלה כאן מחרוזת ג'אווה.  שאלות ותשובות לראיון, חלק 2 - 1

12. כתוב פונקציה למציאת הפלינדרום הארוך ביותר במחרוזת נתונה

מחרוזת יכולה להכיל מחרוזות פלינדרום, ומציאת הפלינדרום הארוך ביותר היא עניין של תכנות. נקודת המפתח כאן היא שמאמצע כל פלינדרום, אם נלך ימינה ושמאלה לפי תו 1, זה תמיד יהיה אותו תו. לדוגמה, 12321, האמצע הוא 3, ואם נמשיך לנוע מהמיקום הנוכחי בשני הכיוונים, נקבל 2 ואז 1. אנו משתמשים בלוגיקה דומה בתוכנית Java שלנו כדי למצוא את הפלינדרום הארוך ביותר. עם זאת, אם אורך הפלינדרום זוגי, גם אורך האמצע הוא זוגי, ולכן עלינו לוודא שזה מסופק גם בתוכנית שלנו, למשל, 12333321, האמצע הוא 33, ואם נמשיך לנוע בשני הכיוונים, נקבל 3, 2 ו-1. בתוכנית שלנו, אנו עוברים על המחרוזת המתקבלת עם האמצע במקום הראשון ובודקים את התווים השמאלי והימני. יש לנו גם שני משתנים גלובליים לאחסון המיקום ההתחלתי של הפלינדרום. אנחנו צריכים גם לבדוק אם כבר נמצא פלינדרום ארוך יותר, מכיוון שאנו יכולים למצוא מספר פלינדרום במחרוזת נתונה. להלן תוכנית לדוגמה שעובדת מצוין בכל המקרים. נוכל לשפר את הקוד לעיל על ידי העברת לולאת ה-while לשיטה נפרדת, אבל את החלק הזה אשאיר לך. אנא הודע לי אם יש לך יישום טוב יותר או אם התוכנית נכשלת בדרך כלשהי.
package com.journaldev.util;

public class LongestPalindromeFinder {

    public static void main(String[] args) {
        System.out.println(longestPalindromeString("1234"));
        System.out.println(longestPalindromeString("12321"));
        System.out.println(longestPalindromeString("9912321456"));
        System.out.println(longestPalindromeString("9912333321456"));
        System.out.println(longestPalindromeString("12145445499"));
    }

    public static String longestPalindromeString(String in) {
        char[] input = in.toCharArray();
        int longestPalindromeStart = 0;
        int longestPalindromeEnd = 0;

        for (int mid = 0; mid < input.length; mid++) {
            // для случая нечетного палиндрома How 12321, 3 будет серединой
            int left = mid-1;
            int right = mid+1;
            // нам необходимо двигаться влево и вправо на 1 позицию до конца
            while (left >= 0 && right < input.length) {
                // ниже проверка, является ли это палиндромом
                if (input[left] == input[right]) {
                    // обновление глобальных позиций, только если палиндром длиннее имеющегося
                    if (right - left > longestPalindromeEnd
                            - longestPalindromeStart) {
                        longestPalindromeStart = left;
                        longestPalindromeEnd = right;
                    }
                }
                left--;
                right++;
            }
            // для четного палиндрома у нас должна быть подобная логика с размером середины 2
            // для этого мы начнем на одну позицию правее
            left = mid-1;
            right = mid + 2;// к примеру, для 12333321 мы выбрали 33 в качестве середины
            while (left >= 0 && right < input.length)
            {
                if (input[left] == input[right]) {
                    if (right - left > longestPalindromeEnd
                            - longestPalindromeStart) {
                        longestPalindromeStart = left;
                        longestPalindromeEnd = right;
                    }
                }
                left--;
                right++;
            }
        }
        // теперь у нас есть позиции для самого длинного палиндрома
        return in.substring(longestPalindromeStart, longestPalindromeEnd + 1);
    }
}
התוכנית תוציא את הדברים הבאים:
1
12321
12321
12333321
454454

13. מה ההבדלים בין String, StringBuffer ו-StringBuilder

מחרוזת היא בלתי ניתנת לשינוי וסופית בג'אווה, כך שכל מניפולציות המחרוזות שלנו תמיד ייצרו מחרוזת חדשה. מניפולציה של מחרוזת היא עתירת משאבים, כך ש-Java מספקת שתי מחלקות שימושיות למניפולציה של מחרוזות - StringBufferו StringBuilder. StringBufferוהם StringBuilderמחלקות הניתנות לשינוי. פעולות עם StringBufferבטוחות בשרשור ומסונכרנות, אבל השיטות StringBuilderאינן בטוחות בשרשור. אז כאשר שרשורים מרובים עובדים על אותה מחרוזת אנחנו צריכים להשתמש StringBuffer, אבל בסביבה עם שרשור בודד אנחנו צריכים להשתמש StringBuilder. StringBuilderפרודוקטיבי יותר מאשר StringBufferבגלל שהוא לא עמוס בסנכרון.

14. מדוע מחרוזת ניתנת לשינוי וסופית בג'אווה?

ישנם מספר יתרונות לבלתי משתנה של מחרוזת:
  1. איגום מחרוזות אפשרי רק בגלל שהמחרוזת אינה ניתנת לשינוי ב-Java, ולכן המכונה הוירטואלית חוסכת הרבה שטח ערימה שכן משתני מחרוזת שונים מצביעים על אותו משתנה במאגר. אם מחרוזת לא הייתה ניתנת לשינוי, אזי התמחות מחרוזת לא הייתה אפשרית, מכיוון שאם משתנה כלשהו משנה את ערכו, משתנים אחרים המתייחסים למחרוזת זו יושפעו גם הם.

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

  3. מכיוון שהמחרוזת אינה ניתנת לשינוי, היא בטוחה לחוטים וניתן לשתף מופע אחד של המחרוזת בין חוטים שונים. זה ימנע סנכרון לבטיחות חוטים, מיתרים בטוחים לחלוטין.

  4. משתמשים במחרוזות ב-Java classloaderוהאי-שינוי מבטיח שהמחלקה נטענת בצורה נכונה באמצעות Classloader. לדוגמה, חשבו על מופע מחלקה כאשר אתם מנסים לטעון java.sql.Connectionמחלקה, אך ערך ההפניה משתנה למחלקה myhacked.Connectionשעלולה לעשות דברים לא רצויים למסד הנתונים שלכם.

  5. מכיוון שהמחרוזת אינה ניתנת לשינוי, היא hashcodeנשמרת במטמון בזמן היצירה ואין צורך לחשב אותה שוב. זה הופך את המחרוזת למועמדת מצוינת למפתח Mapוהעיבוד שלה יהיה מהיר יותר ממקשים אחרים HashMap. זו הסיבה מדוע מחרוזת היא האובייקט הנפוץ ביותר המשמש כמפתח HashMap.

15. איך לפצל מיתר לחלקים?

אנו יכולים להשתמש בשיטה split(String regex)כדי לפצל מחרוזת למערך של מחרוזות באמצעות ביטוי רגולרי כמפריד.
import java.util.Arrays;

public class JavaSplitString {
    public static void main(String[] args) {
        String line = "I am a java developer";
        String[] words = line.split(" ");
        String[] twoWords = line.split(" ", 2);
        System.out.println("String split with delimiter: "+Arrays.toString(words));
        System.out.println("String split into two: "+Arrays.toString(twoWords));
        //split string delimited with special characters
        String wordsWithNumbers = "I|am|a|java|developer";
        String[] numbers = wordsWithNumbers.split("\\|");
        System.out.println("String split with special character: "+Arrays.toString(numbers));
    }
}
השיטה split(String regex, int numOfStrings)היא שיטה עמוסה מדי לפיצול מחרוזת למספר מוגדר של שורות. אנו יכולים להשתמש בקו נטוי כדי להשתמש בתווים מיוחדים של ביטוי רגולרי כתווים רגילים. התוכנית תוציא את הדברים הבאים:
String split with delimiter: [I, am, a, java, developer]
String split into two: [I, am a java developer]
String split with special character: [I, am, a, java, developer]

16. מדוע מערך מחרוזות עדיף על מחרוזת לאחסון סיסמה?

מחרוזת ניתנת לשינוי ב-Java והיא מאוחסנת במאגר מחרוזות. ברגע שהוא נוצר, הוא נשאר בבריכה עד שהוא נאסף, כך שכאשר אנו חושבים שסיימנו עם הסיסמה, הוא נשאר זמין בזיכרון לזמן מה ואין דרך להימנע מכך. זהו סיכון אבטחה מכיוון שכל מי שיש לו גישה ל-dump הזיכרון יוכל למצוא את הסיסמה בטקסט ברור. אם נשתמש במערך תווים לאחסון הסיסמה, נוכל לנקות אותה לאחר שנסיים איתה. כך נוכל לשלוט כמה זמן הוא נשאר בזיכרון, ולמנוע את סיכוני האבטחה הגלומים במחרוזת.

17. איך בודקים שתי מחרוזות לדמיון בג'אווה?

ישנן שתי דרכים לבדוק אם שתי מחרוזות שוות ערך - שימוש ==באופרטור " " או שימוש ב- equals. כאשר אנו משתמשים באופרטור " ==", הוא בודק את הערך של המחרוזת כהפניה, אך בתכנות רוב הזמן אנו בודקים את שווי המחרוזת רק עבור הערך. לכן, עלינו להשתמש בשיטת שווה כדי לבדוק שתי מחרוזות לשוויון. יש גם שיטה equalsIgnoreCaseשאנחנו יכולים להשתמש בה כדי להתעלם מהמקרה.
String s1 = "abc";
String s2 = "abc";
String s3= new String("abc");
System.out.println("s1 == s2 ? "+(s1==s2)); //true
System.out.println("s1 == s3 ? "+(s1==s3)); //false
System.out.println("s1 equals s3 ? "+(s1.equals(s3))); //true

18. מהי בריכת מיתר?

כפי שהשם מרמז, מאגר מחרוזות הוא אוסף של מחרוזות המאוחסנות בערימת Java. אנו יודעים שזו Stringמחלקה מיוחדת בג'אווה ונוכל ליצור אובייקטים מהמחלקה הזו באמצעות האופרטור החדש בדיוק כפי שאנו יכולים ליצור אובייקטים על ידי מתן ערך של מחרוזת במירכאות כפולות. התרשים שלהלן מסביר כיצד מאגר המחרוזות מוקצה בערימת Java ומה קורה כאשר אנו משתמשים בדרכים שונות ליצירת מחרוזות. מחרוזת ג'אווה.  שאלות ותשובות לראיון, חלק 2 - 2איגום מחרוזות אפשרי אך ורק בגלל חוסר השינוי של מחרוזות של Java ויישום הרעיון של התמחות מחרוזת. בריכת מיתרים היא גם דוגמה לתבנית Flyweight. מאגר מיתרים עוזר לחסוך הרבה זיכרון, אבל מצד שני, יצירת שורה לוקחת יותר זמן. כאשר אנו משתמשים במירכאות כפולות ליצירת מחרוזת, היא תחילה מחפשת מחרוזת במאגר עם אותו ערך, אם היא נמצאת אז פשוט מחזירה הפניה, אחרת נוצרת מחרוזת חדשה במאגר ולאחר מכן מחזירה הפניה. עם זאת, כאשר אנו משתמשים באופרטור החדש, אנו מכריחים את המחלקה Stringליצור אובייקט מחרוזת חדש, ואז נוכל להשתמש בשיטה intern()כדי להכניס את המחרוזת לבריכה, או לקבל הפניה מהבריכה לאובייקט אחר Stringעם אותו ערך. להלן דוגמה המראה כיצד פועלת בריכת המיתרים.
public class StringPool {
    public static void main(String[] args) {
        String s1 = "Cat";
        String s2 = "Cat";
        String s3 = new String("Cat");

        System.out.println("s1 == s2 :"+(s1==s2));
        System.out.println("s1 == s3 :"+(s1==s3));
    }
}
התוכנית תוציא את הדברים הבאים:
s1 == s2 :true
s1 == s3 :false

19. מה עושה שיטת intern()?

כאשר השיטה intern()נקראת, אם מאגר המחרוזות כבר מכיל מחרוזת שווה ערך לאובייקט שלנו, כפי שאומת על ידי השיטה equals(Object), אזי מוחזרת הפניה למחרוזת מהמאגר. אחרת, אובייקט המחרוזת מתווסף למאגר ומוחזרת הפניה לאותו אובייקט. שיטה זו תמיד מחזירה מחרוזת בעלת ערך זהה למחרוזת הנוכחית, אך מבטיחה שהיא תהיה מחרוזת ממאגר המחרוזות הייחודיות. להלן דוגמה כיצד השיטה פועלת intern():
public class StringPool {
    public static void main(String[] args) {
        String a = "string a";
        String b = new String("string a");
        String c = b.intern();

        System.out.println(a == b);
        System.out.println(b == c);
        System.out.println(a == c);
    }
}
Программа выведет следующее:false
false
true

20. האם חוט מחרוזות בטוח ב-Java?

המחרוזות אינן ניתנות לשינוי, ולכן איננו יכולים לשנות את ערכן בתוכנית. לכן הם בטוחים לחוטים וניתן להשתמש בהם בבטחה בסביבה מרובת הברגה.

21. מדוע String הוא מפתח פופולרי ב-HashMap ב-Java?

מכיוון שהמחרוזות אינן ניתנות לשינוי, קוד ה-hash שלהן נשמר במטמון בזמן היצירה ואינו דורש חישוב מחדש. זה הופך מחרוזות למועמד מצוין למפתח Mapוהן מעובדות מהר יותר מאובייקטי מפתח אחרים HashMap. זו הסיבה שהמחרוזות משמשות בעיקר כמפתחות HashMap. אני מקווה שהשאלות המפורטות במאמר זה יעזרו לך בראיונות שלך, אנא הודע לי אם פספסתי משהו. קישור למאמר המקורי מחבר: Pankaj Kumar
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION