П'ять речей, яких ви не знали про

API колекцій Java багатьма Java-розробниками був сприйнятий як дуже потрібна заміна стандартних масивів, що усуває їх недоліки. Асоціювання колекцій в першу чергу з ArrayList не є великою помилкою, однак ті, хто шукає, можуть відкрити для себе в колекціях набагато більше.

Про цю серії статей

Ви думаєте, що знаєте, як програмувати на Java? Насправді більшість розробників лише поверхово знайомляться з платформою Java, дізнаючись лише те, що необхідно для виконання роботи. У цьому циклі статей Тед Ньювард поглиблюється в основну функціональність платформи Java, щоб розповісти про маловідомі факти, знання яких може допомогти при вирішенні найскладніших завдань програмування.

Аналогічно, хоча Map (і його поширена реалізація HashMap) є відмінним засобом створення пар ім'я-значення або ключ-значення, немає причини обмежувати себе цими знайомими інструментами. Ви можете виправити багато потенційно помилкового коду, вибравши правильний API або навіть правильну колекцію.

1. Колекції витісняють масиви

Наприклад, щоб сформувати з вмісту масиву рядок, потрібно обійти весь масив і об'єднати вміст усіх його елементів в одному об'єкті String. тоді як в колекціях є готова реалізація методу toString ().

За рідкісним винятком, рекомендується кожен вступник до вас масив якомога швидше конвертувати в колекцію. Це призводить до питання: як легше всього виконати таке перетворення? Виявляється, API колекцій Java робить цей задачу вельми простий, як показано в лістингу 1:

Лістинг 1. ArrayToList

Зверніть увагу, що повертається колекція List є незмінною, тому спроба додати нові елементи призведе до викидання виключення UnsupportedOperationException.

І, так як в методі Arrays.asList () для додавання елементів List використовується параметр varargs. з його допомогою можна легко створювати колекції List з щойно створених об'єктів.

2. Ітерірованіе неефективно

Нерідко буває потрібно перемістити вміст однієї колекції (особливо такий, яка була створена з масиву) в іншу колекцію, або видалити деяку частину об'єктів з колекції.

Природне рішення тут - це обійти (за допомогою ітератора) колекцію і додати або видалити потрібні елементи. Однак не варто так робити.

Ітерірованіе в даному випадку має кілька серйозних недоліків:

  • неефективно змінювати розмір колекції при кожному додаванні або видаленні елементу;
  • кожен раз при отриманні блокування, виконанні операції і звільнення блокування можуть виникати проблеми, пов'язані з одночасним доступом;
  • можуть виникнути гоночні ситуації при доступі до колекції з інших потоків під час додавання або видалення елементів.

Всіх цих проблем можна уникнути, використовуючи методи addAll або removeAll. передаючи в них колекції, що містять елементи, які потрібно додати або видалити.

3. Цикл for для будь-якого об'єкта з інтерфейсом Iterable

Розширений цикл for - це одне з найбільш корисних поліпшень, доданих в Java 5, завдяки якому був прибраний останній бар'єр для роботи з колекціями в Java.

Раніше розробникам доводилося вручну отримувати Iterator. потім за допомогою методу next () отримувати об'єкт, на який цей Iterator вказує, а потім перевіряти за допомогою методу hasNext (). чи є ще об'єкти. Починаючи з Java 5, замість цього можна використовувати цикл for, в якому все це обробляється автоматично.

Фактично цей цикл можна використовувати при роботі з будь-яким об'єктом, що реалізує інтерфейс Iterable. а не тільки з колекціями.

У лістингу 2 показаний спосіб зробити список дітей об'єкта Person доступним у вигляді ітератора. Замість того щоб передавати посилання на внутрішній List (яка дозволить викликає стороні ззовні об'єкта Person додавати йому дітей - що не сподобалося б більшості батьків), тип Person реалізує інтерфейс Iterable. Такий підхід також дозволяє за допомогою розширеного циклу for обходити дітей об'єкту класу Person.

Лістинг 2. Розширений цикл for: покажіть мені ваших дітей

Використання Iterable має деякі очевидні недоліки при моделюванні предметної області, так як за допомогою методу iterator () таким чином можна підтримувати тільки одну колекцію об'єктів. Однак для таких випадків, як цей, де колекція дітей напрошується сама, інтерфейс Iterable робить програмування предметної області набагато простіше і очевидніше.

4. Класичні і призначені для користувача алгоритми

Чи хотіли ви коли-небудь обійти колекцію, але не в прямому, а в зворотному порядку? Саме тут може стати в нагоді алгоритм колекцій Java.

У лістингу 2 діти об'єкта Person перераховуються в порядку, в якому вони були передані, однак тепер ми хочемо перерахувати їх в зворотному порядку. Можна було б написати ще один цикл for і вставити в ньому об'єкти в нову колекцію ArrayList в зворотному порядку, однак це може втомити при використанні цього коду третій або четвертий раз.

Тут можна застосувати незаслужено рідко використовується алгоритм, показаний в лістингу 3.

Лістинг 3. ReverseIterator

У класі Collections є кілька таких "алгоритмів" - статичних методів, які приймають в якості параметра об'єкт класу Collections і виконують над цією колекцією незалежні від реалізації дії.

Більш того, алгоритми, представлені в класі Collections. безумовно не є останнім словом в чудовій архітектурі цього API. Наприклад, я вважаю за краще, щоб методи не змінювали вміст (переданої в них колекції) безпосередньо. Для таких випадків передбачена можливість написання призначених для користувача алгоритмів, один з яких в якості прикладу показаний в лістингу 4.

Лістинг 4. Спрощуємо ReverseIterator

5. Розширюємо API колекцій

Наведений вище призначений для користувача алгоритм ілюструє останню особливість API колекцій в Java: його можна розширювати і видозмінювати відповідно до потреб розробників.

Наприклад, припустимо, нам потрібно, щоб список дітей об'єкту класу Person завжди був впорядкований за віком. Можна написати алгоритм, який буде кожен раз сортувати список (наприклад, за допомогою методу Collections.sort), однак було б набагато краще, якби клас колекції виконував цю сортування сам.

Насправді, можливо, вам немає діла до того, в якому порядку об'єкти були вставлені в колекцію (що має принципове значення для колекції List). Можливо, ви просто хочете зберігати їх згідно з порядком сортування.

Жодна колекція з пакета java.util не задовольняє цим вимогам, однак можна досить легко написати потрібний клас самостійно. Все, що потрібно, - це створити інтерфейс, що описує абстрактне поведінку, яке має забезпечувати колекція. Наприклад, інтерфейс SortedCollection. має виключно поведінковий призначення (лістинг 5).

Лістинг 5. SortedCollection

Написати реалізацію даного інтерфейсу дуже просто (лістинг 6).

Лістинг 6. ArraySortedCollection

Ця реалізація була написана «на коліні», без думки про оптимізацію і, очевидно, її варто трохи переробити. Однак суть в тому, що API колекцій в Java ні в якому разі не є остаточним рішенням для всього, що пов'язано з колекціями. Він потребує розширення і заохочує його.

Звичайно, деякі розширення будуть з розряду рішень "великої потужності", наприклад ті, що з'явилися в пакеті java.util.concurrent. Однак інші розширення можуть бути досить прості - наприклад, написання користувацького алгоритму або простого розширення існуючого класу Collection.

Розширення API колекцій Java може здатися надзвичайно важким, проте, почавши займатися цим, ви виявите, що це зовсім не так складно, як ви думали.

На закінчення

Як і API сериализации, API колекцій Java сповнений тонкощів, тому ми не закриваємо цю тему. У наступній статті циклу 5 речей ми розповімо ще про п'ять способах отримати більше від API колекцій Java.

Схожі статті