JavaRush /Java блог /Random UA /Топ 10 питань про колекції Java
FedoraLinux
21 рівень
Москва

Топ 10 питань про колекції Java

Стаття з групи Random UA
Стаття є перекладом статті " Top 10 questions o Java Collections " . Нижче представлені найпопулярніші питання з приводу колекцій Java, задані та обговорені на Stackowerflow. До того, як Ви подивіться на ці питання, добре подивитися діаграму ієрархії класів. 1. Коли використовувати LinkedList замість ArrayList? ArrayList за фактом є масивом, його елементи можуть бути доступні безпосередньо за індексом. У разі переповнення масиву виникає необхідність у новому, що має більше місця. Розміщення та переміщення всіх елементів займатиме O(n) часу. Також, необхідно додавання та видалення елементів для пересування існуючих елементів у масиві. Це, можливо, найбільша незручність у використанні ArrayList. LinkedList - це подвійний перелік посилань на елементи. Таким чином, для доступу до елемента в центрі доводиться шукати з самого початку і до кінця аркуша. З іншого боку, додавання та видалення елемента в LinkedList швидше з тієї причини, що ці операції лише змінюють сам список. Найгірші моменти часу порівнюються нижче:
Метод Arraylist LinkedList
get(index) O(1) O(n)
add(E) O(n) O(1)
add(E, index) O(n) O(n)
remove(index) O(n) O(n)
Iterator.remove() O(n) O(1)
Iterator.add(E) O(n) O(1)
Незважаючи на час роботи, використання пам'яті має бути продуманим індивідуально для великих списків. У LinkedList кожен вузол повинен мати щонайменше два додаткові покажчики, щоб пов'язувати попередній і наступний вузли, тоді як ArrayList потрібен лише масив елементів. Більше порівнянь списків ArrayList, LinkedList та Vector (англ.) . 2. Ефективний еквівалент видалення елементів під час ітерації колекції Єдиний коректний шлях для модифікації (видалення елементів) колекції під час ітерації - це використання Iterator.remove() . Наприклад: Найпоширеніший варіант помилки: Ви отримаєте ConcurrentModificationException Iterator itr = list.iterator(); while(itr.hasNext()) { // do something itr.remove(); } for(Integer i: list) { list.remove(i); } під час роботи коду вище. Так відбувається через те, що ітератор був згенерований для переміщення по всьому списку, але в той же час лист змінюється викликом Iterator.remove(). Як написано в документації з цього виключення,
"Це не є цілком можливою для одного треку до зміни колекції, коли інша річ є помітною над ним."
У цілому нині, неприпустима ситуація, коли одна нитка (thread) змінює колекцію тоді, як інша нитка проходить нею. 3. Як конвертувати List масив int[]? Найлегшим шляхом для цього є використання ArrayUtils , що знаходиться в бібліотеці Apache Commons Lang . int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0])); У JDK немає скорочення цього висловлювання. Запам'ятайте, що Ви не можете використовувати List.toArray() тому, що цей вираз конвертує List в Integer[] (який не є примітивним типом, прим. переклад). Правильним шляхом буде наступний варіант: int[] array = new int[list.size()]; for(int i=0; i < list.size(); i++) { array[i] = list.get(i); } 4. Як конвертувати масив int[] у List? Найпростішим способом також є використання ArrayUtils у бібліотеці Apache Commons Lang , як і вище. List list = Arrays.asList(ArrayUtils.toObject(array)); Також, у JDK немає скорочення цього висловлювання. 5. Як краще фільтрувати колекцію? Ви можете використовувати сторонні пакети, такі як Guava або Apache Commons Lang для збільшення функціоналу. Обидва ці пакети мають метод filter() (у класі Collections2 від Guava і CollectionUtils від Apache). Метод filter() поверне елементи, які збігаються із взятим Предикатом (Predicate). У JDK все складніше. Хороші новини полягають у тому, що Java 8 предикати будуть додані ( вже додані , прим. переклад.), але зараз Вам необхідно використовувати Iterator для переміщення по всій колекції. int[] array = {1,2,3,4,5}; List list = new ArrayList (); for(int i: array) { list.add(i); } Iterator itr = list.iterator(); while(itr.hasNext()) { int i = itr.next(); if (i > 5) { // filter all ints bigger than 5 itr.remove(); } } Звичайно, Ви можете імітувати той шлях, яким йдуть Guava та Apache, познайомившись із новим інтерфейсом Predicate. Тепер ми можемо використовувати наступний код для фільтрації колекції: 6. Як легко конвертувати List у Set? Існує два шляхи для цього залежно від того, як Ви хочете визначати рівність. Перший шматок коду містить список у HashSet. Дублікат у такому випадку визначається в основному за hashCode(). Як правило, це працюватиме. Але якщо Вам потрібно враховувати шлях порівняння, то краще використовувати другу частину коду, де Ви можете визначити Ваш власний компаратор. 7. Як я можу видалити повторювані елементи з ArrayList? public interface Predicate { boolean test(T o); } public static void filter(Collection collection, Predicate predicate) { if ((collection != null) && (predicate != null)) { Iterator itr = collection.iterator(); while(itr.hasNext()) { T obj = itr.next(); if (!predicate.test(obj)) { itr.remove(); } } } } filter(list, new Predicate () { public boolean test(Integer i) { return i <= 5; } }); Set set = new HashSet (list); Set set = new TreeSet (aComparator); set.addAll(list); Це питання певною мірою пов'язане з питанням вище. Якщо вам не має значення порядок елементів в ArrayList, розумним ходом буде помістити лист у набір (Set) для видалення дубліуатів, а потім повернути назад до списку (List). Нижче наведено приклад. Якщо для Вас має значення порядок елементів, то порядок може бути забезпечений шляхом розміщення списку в LinkedHashSet , який є стандартним JDK. 8. Сортована колекція Існує кілька шляхів для підтримки сортованої колекції Java. Усі з них забезпечують колекцію в натуральному порядку або за вказаним компаратором. У випадку з натуральним порядком, Вам також потрібно реалізувати інтерфейс Comparable в елементі. ArrayList** list = ... // initial a list with duplicate elements Set set = new HashSet (list); list.clear(); list.addAll(set);
  1. Collections.sort() може відсортувати List. Як зазначено в документації Java, це сортування стабільне і гарантує продуктивність n log(n).
  2. PriorityQueue забезпечує впорядковану чергу. Різниця між PriorityQueue і Collections.sort() полягає в тому, що PriorityQueue підтримує порядок черги весь час, але Ви можете отримати лише перший елемент черги. Ви не можете отримати випадковий доступ до елемента, наприклад, як PriorityQueue.get(4).
  3. Якщо немає дублікатів у колекції, можна вибрати TreeSet . Так само, як і PriorityQueue, TreeSet підтримує впорядкований набір весь час. Ви можете отримати найменший або найбільший елемент TreeSet, але ви все одно не можете мати випадковий доступ до елементів.
Простіше кажучи Collections.sort() забезпечує одноразовий упорядкований список. PriorityQueue та TreeSet підтримує впорядковану колекцію постійно, за що доводиться платити відсутністю індексованого доступу до елементів. 9. Collections.emptyList() або новий екземпляр Те саме питання застосовується до emptyMap() і emptySet(). Обидва методи повертають порожній список, але Collections.emptyList() незаперечний (immutable) список. Це означає, що ви не можете додавати нові елементи до "порожнього" списку. На тлі кожен виклик методу Collections.emptyList() фактично не створює новий екземпляр порожнього списку. Натомість воно використовуватиме знову вже існуючий порожній екземпляр. Якщо Ви знайомі із синглтоном (Singleton, тиц, прим. переклад.) як із патерном проектування, Ви повинні зрозуміти що мають на увазі. Це має Вам дати більшу продуктивність, якщо викликається часто. 10 Копіювання колекції, Collections.copy() Існує два способи копіювання вихідного списку до призначеного. Один шлях – використання конструктора ArrayList. Інший спосіб - використання методу Collections.copy () . Зверніть увагу на перший рядок: ми виділяємо список як мінімум такої ж довжини, що й довжина вихідного списку, тому що в документації Java про колекції сказано: ArrayList dstList = new ArrayList (srcList);
The list list must be at least as long as the source list.
Що означає, що кінцевий список має бути не коротшим, ніж вихідний. Обидва методи є поверхневим копіюванням (shallow copy). Тож яка різниця між цими двома методами? По-перше, Collections.copy() не перерозподілятиме місткість колекції dstList, навіть якщо dstList не матиме достатньо простору для утримання всіх елементів з srcList. Замість цього, він буде кидати IndexOutOfBoundsException . Можна спитати, чи є користь від цього. Причина в тому, що це гарантує, що метод запускається лінійно за часом. Також це підходить у випадку, коли Ви хочете використовувати знову масиви, а не виділяти знову пам'ять у конструкторі ArrayList. Замість ув'язнення ArrayList dstList = new ArrayList (srcList.size()); Collections.copy(dstList, srcList); Якщо після прочитання статті у Вас ще залишабося питання, сміливо ставте їх у коментарі. Також, якщо Ви знайшли якусь неточність у перекладі або ще якусь помилку, то пишіть у ЛЗ, буде виправлена, а Вам буде спасибі. Оригінал.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ