Колекції в Java є основою мови, вони забезпечують роботу таких структур, як прості списки і складні багатозадачні дані. Якщо ви готуєтесь до співбесіди з Java, питання про колекції майже гарантовані. Тут я розгляну дев'ять складних та цікавих питань на цю тему, а також надам практичні приклади коду, щоб допомогти вам успішно пройти співбесіду.
1. Як HashMap
обробляє колізії всередині?
Чому це запитують: Розуміння того, як обробляються колізії, важливе для розуміння ефективності роботи HashMap
.
Відповідь: HashMap
використовує кошики (buckets) для зберігання пар ключ-значення. Один кошик може містити кілька елементів, якщо кілька ключів хешуються в той самий індекс. Спочатку ці елементи зберігаються як зв'язаний список. Якщо кількість елементів у кошику перевищує певний поріг (за замовчуванням 8), список перетворюється на збалансоване дерево для оптимізації часу доступу.
Питання для продовження: Що відбувається, якщо розмір кошика досягає або перевищує поріг?
Відповідь на продовження: Коли розмір кошика досягає порогу (за замовчуванням 0,75 * ємність), HashMap
змінює розмір, подвоюючи ємність та перевизначаючи хешування всіх ключів у нові кошики. Це забезпечує, що середня складність операцій get/put залишається O(1).
2. Як реалізувати власний Comparator
для сортування списку об'єктів за кількома полями?
Чому це запитують: Показує розуміння ланцюжків Comparator
.
Приклад коду:
import java.util.*;
class Employee {
String name;
int age;
double salary;
Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return name + " (Age: " + age + ", Salary: " + salary + ")";
}
}
public class ComparatorChainingExample {
public static void main(String[] args) {
List employees = Arrays.asList(
new Employee("Alice", 30, 70000),
new Employee("Bob", 25, 50000),
new Employee("Charlie", 30, 80000)
);
employees.sort(Comparator
.comparing((Employee e) -> e.age)
.thenComparing((Employee e) -> e.salary, Comparator.reverseOrder()));
System.out.println("Sorted Employees: " + employees);
}
}
3. Як виявити та обробити ConcurrentModificationException
в багатозадачному середовищі?
Чому це запитують: Перевіряє розуміння безпеки потоків у колекціях.
Приклад коду:
import java.util.ArrayList;
import java.util.Iterator;
public class ConcurrentModificationExample {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add(1);
list.add(2);
Thread thread1 = new Thread(() -> {
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
list.add(3); // Це викличе ConcurrentModificationException
}
});
Thread thread2 = new Thread(() -> list.add(4));
thread1.start();
thread2.start();
}
}
Рішення: Використовуйте колекції для багатозадачності, такі як CopyOnWriteArrayList
.
Приклад рішення:
import java.util.concurrent.CopyOnWriteArrayList;
public class ConcurrentCollectionExample {
public static void main(String[] args) {
CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
list.add(1);
list.add(2);
Thread thread1 = new Thread(() -> {
for (Integer i : list) {
System.out.println(i);
list.add(3); // Тут не буде виключення
}
});
Thread thread2 = new Thread(() -> list.add(4));
thread1.start();
thread2.start();
}
}
4. У чому різниця між IdentityHashMap
та HashMap
?
Чому це запитують: Перевіряє глибоке розуміння хеш-таблиць.
Відповідь: IdentityHashMap
використовує порівняння за посиланням (==
) для ключів, тоді як HashMap
використовує метод equals()
для порівняння.
5.
5. Як TreeMap
обробляє нульові ключі та значення?
Чому це запитують: Показує особливості поведінки TreeMap
.
Відповідь:
TreeMap
не дозволяє використовувати нульові ключі.- Він дозволяє мати кілька нульових значень.
Приклад коду:
import java.util.TreeMap;
public class TreeMapNullKeyExample {
public static void main(String[] args) {
TreeMap map = new TreeMap<>();
map.put("A", null);
map.put("B", null);
System.out.println("TreeMap: " + map);
}
}
6. Як LinkedHashMap
зберігає порядок вставки? Чи можна перевизначити цю поведінку?
Чому це запитують: Перевіряє практичне розуміння роботи LinkedHashMap
.
Відповідь: LinkedHashMap
використовує двозв'язний список для збереження порядку вставки. За допомогою параметра accessOrder
, встановленого в true
в конструкторі, можна змінити порядок на основі доступу.
Приклад коду:
import java.util.LinkedHashMap;
public class LinkedHashMapExample {
public static void main(String[] args) {
LinkedHashMap map = new LinkedHashMap<>(16, 0.75f, true);
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
map.get("A"); // Доступ до "A"
System.out.println("LinkedHashMap: " + map);
}
}
7. Що відбувається, коли ви намагаєтесь змінити колекцію під час ітерації?
Чому це запитують: Охоплює поведінку fail-fast
.
Відповідь: Ви отримаєте ConcurrentModificationException
, якщо колекція буде змінена структурно (наприклад, додавання або видалення елементів) під час ітерації за допомогою не-конкурентної колекції.
Рішення: Використовуйте Iterator.remove()
для безпечного видалення.
Приклад коду:
import java.util.ArrayList;
import java.util.Iterator;
public class FailFastExample {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("A");
list.add("B");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove(); // Безпечне видалення
}
System.out.println("Modified List: " + list);
}
}
8. Який за замовчуванням коефіцієнт заповнення (load factor
) у HashMap
, і чому він встановлений на 0.75?
Чому це запитують: Перевіряє розуміння оптимізації продуктивності в HashMap
.
Відповідь: Коефіцієнт заповнення 0.75 досягає балансу між часом (повторне хешування) і простором (додаткові кошики).
Питання для продовження: Що відбувається, якщо коефіцієнт заповнення встановити на більше значення?
Відповідь на продовження: Якщо коефіцієнт заповнення більший, HashMap
буде рідше змінювати розмір, що зекономить час при вставках, але може призвести до більших часів доступу через більші колізії в кошиках.
9. Як PriorityQueue
реалізує свою внутрішню структуру?
Чому це запитують: Перевіряє знання про структури даних на основі купи.
Відповідь: PriorityQueue
реалізовано як бінарну купу, де пріоритет визначає порядок купи (за замовчуванням мін-купа).
Приклад коду:
import java.util.PriorityQueue;
public class PriorityQueueExample {
public static void main(String[] args) {
PriorityQueue pq = new PriorityQueue<>();
pq.add(10);
pq.add(5);
pq.add(15);
while (!pq.isEmpty()) {
System.out.println(pq.poll()); // Виводить в відсортованому порядку
}
}
}
Оволодіння нюансами фреймворку колекцій Java може допомогти вам виділитись на співбесіді. Розуміння внутрішньої роботи, практичних випадків використання та деталей реалізації дозволить вам не лише відповісти на технічні питання, але й писати ефективний і підтримуваний код. Успіхів у програмуванні та удачі на співбесідах!
Для більше таких статей — https://medium.com/@poojaauma
Перекладено з: Tricky and Interesting Java Interview Questions on Collections