JavaRush/Java блог/Random UA/Java String. Питання до співбесіди та відповіді на них, ч...
Andrey
26 рівень

Java String. Питання до співбесіди та відповіді на них, ч.2

Стаття з групи Random UA
учасників
На жаль, стаття не помістилася одним фрагментом, довелося розбити її на дві частини. Початок дивіться тут Java String.  Запитання до співбесіди та відповіді на них, ч.2 - 1

12. Напишіть функцію для знаходження найдовшого паліндрому в даному рядку

Рядок може містити рядки-паліндроми, і знаходження найдовшого паліндрому - це питання програмування. Ключовим моментом тут є те, що з середини будь-якого паліндрому, якщо ми підемо праворуч і ліворуч на 1 символ, це завжди буде однаковим символом. Наприклад, 12321, середина тут 3, і якщо ми продовжимо рух з поточної позиції в обидві сторони, ми отримаємо 2, а потім 1. Ми використовуємо подібну логіку в нашій програмі Java для знаходження найдовшого паліндрому. Однак якщо довжина паліндрому парна, довжина середини теж парна, так що ми повинні переконатися, що в нашій програмі це так само передбачено, наприклад, 12333321, тут середина 33, і якщо ми продовжимо рух в обидва боки, ми отримаємо 3, 2 і 1. У нашій програмі ми проходимо по рядку з серединою на першому місці і перевіряємо лівий і правий символ. Також ми маємо дві глобальні змінні для зберігання початкової позиції паліндрому. Нам також необхідно перевірити наявність вже знайденого довшого паліндрому, оскільки ми можемо знайти кілька паліндромів у даному рядку. Нижче наведено приклад програми, яка добре працює у всіх випадках. Ми можемо вдосконалити наведений код, перемістивши цикл while в окремий метод, але я залишив цю частину для вас. Будь ласка, дайте мені знати, якщо у вас є вдала реалізація, або програма не спрацьовує в якомусь випадку. Ми можемо вдосконалити наведений код, перемістивши цикл while в окремий метод, але я залишив цю частину для вас. Будь ласка, дайте мені знати, якщо у вас є вдала реалізація, або програма не спрацьовує в якомусь випадку. Ми можемо вдосконалити наведений код, перемістивши цикл 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++) {
            // для случая нечетного палиндрома як 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, тому всі наші маніпуляції з рядком завжди будуть створювати новий рядок. Маніпуляції з рядками ресурсомісткі, тому Java забезпечує два корисні класи для маніпуляцій з рядками – StringBufferі StringBuilder. StringBufferі StringBuilderє змінними класами. Операції з StringBufferпотокобезпечними та синхронізованими, а методи StringBuilderне потокобезпечними. Тому коли кілька ниток працюють з одним рядком, ми повинні використовувати StringBuffer, але в однопотоковому ми повинні використовувати StringBuilder. StringBuilderбільш продуктивний, ніж StringBuffer, оскільки не обтяжений синронізацією.

14. Чому рядок незмінний і фіналізований в Java

Є кілька переваг у незмінності рядків:
  1. Рядковий пул можливий тільки тому, що рядок незмінна в Java, таким чином віртуальна машина зберігає багато місця в пам'яті (heap space), оскільки різні рядкові змінні вказують на одну змінну в пулі. Якби рядок не був незмінним, тоді інтернування рядків не було б можливим, тому що якщо яка-небудь змінна змінить значення, це позначиться також і на інших змінних, що посилаються на цей рядок.

  2. Якщо рядок буде змінним, тоді це стане серйозною загрозою безпеці програми. Наприклад, ім'я користувача бази даних та пароль передаються рядком для отримання з'єднання з базою даних та у програмуванні сокетів реквізити хоста та порту передаються рядком. Так як рядок незмінна, його значення не може бути змінено, в іншому випадку будь-який хакер може змінити значення посилання та викликати проблеми безпеки програми.

  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 і зберігається в пулі рядків. З того часу, як вона була створена, вона залишається в пулі, поки не буде видалена збирачем сміття, тому коли ми думаємо, що закінчабо роботу з паролем, він залишається доступним у пам'яті деякий час, і немає способу уникнути цього. Це ризик безпеки, оскільки будь-хто, хто має доступ до дампи пам'яті, зможе знайти пароль у вигляді чистого тексту. Якщо ми використовуємо масив символів для зберігання пароля, ми можемо очистити його після завершення роботи. Таким чином, ми можемо контролювати, як довго він знаходиться в пам'яті, що дозволяє уникнути ризику безпеки, властивого рядку.

17. Як ви перевірите два рядки на подібність у Java?

Є два способи перевірити, чи два рядки є еквівалентними – використовуючи оператор “ ==”, або використовуючи метод equals. Коли ми використовуємо оператор “ ==”, він перевіряє значення рядка, як посилання, але у програмуванні більшу частину часу ми перевіряємо еквівалентність рядка лише значення. Тому ми повинні використовувати метод 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 heap. Ми знаємо, що Stringце спеціальний клас у Java, і ми можемо створювати об'єкти цього класу, використовуючи оператор new так само, як і створювати об'єкти, надаючи значення рядка в подвійних лапках. Діаграма нижче пояснює, як пул рядків розміщується в пам'яті Java heap і що відбувається, коли ми використовуємо різні способи створення рядків. Java String.  Питання до співбесіди та відповіді на них, ч.2 - 2Пул рядків можливий виключно завдяки незмінності рядків у Java та реалізації ідеї інтернування рядків. Пул рядків також є прикладом патерна Пристосуванець (Flyweight). Пул рядків допомагає заощаджувати великий обсяг пам'яті, але з іншого боку, створення рядка займає більше часу. Коли ми використовуємо подвійні лапки для створення рядка, спочатку шукається рядок у пулі з таким самим значенням, якщо знаходиться, то просто повертається посилання, інакше створюється новий рядок у пулі, а потім повертається посилання. Тим не менш, коли ми використовуємо оператор new, ми примушуємо клас 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. Чому рядок є популярним ключем у HashMap Java?

Оскільки рядки незмінні, їх хешкод кешується в момент створення і не вимагає повторного перерахунку. Це робить рядки відмінним кандидатом для ключа Mapі вони обробляються швидше, ніж інші об'єкти-ключі HashMap. Ось чому рядки переважно використовуються як ключі HashMap. Сподіваюся, що запитання, перелічені в цій статті, допоможуть вам на співбесідах, будь ласка, дайте мені знати, якщо я щось пропустив. Посилання на оригінальну статтю Автор: Pankaj Kumar
Коментарі
  • популярні
  • нові
  • старі
Щоб залишити коментар, потрібно ввійти в систему
Для цієї сторінки немає коментарів.