При об’єктно орієнтованому програмуванні доводиться працювати з великою кількістю об’єктів. Зручно мати засоби групування об’єктів. Для цих цілей в Java розроблено набір інтерфейсів і класів на їх основі під назвою колекції . В основі ієрархії колекцій знаходиться інтерфейс Collection.

Структура колекцій Згадаймо, що інтерфейс – це клас, який не містить реалізації методів, а лише їхні оголошення. Можна реалізувати безліч реалізацій інтерфейсу. Програмісту, який використовуватиме ці реалізації, достатньо знати базовий інтерфейс для роботи з його реалізаціями, тобто знати методи, які передбачає даний інтерфейс.

Collection – базовий інтерфейс, крім нього на його основі в структурі колекцій є ще декілька інтерфейсів, які розширюють базовий інтерфейс Collection. Зокрема, List, Set та SortedSet. Окремо виділяють ще інтерфейс Map. Він не походить на пряму від інтерфейсу Collection, проте його також відносять до колекцій. На їх основі створено набір класів, які згодяться програмістам для більшості випадків роботи з набором об’єктів. Тож вам не прийдеться самим їх реалізовувати.

Якщо вам цікаво, для чого стільки різних класів в колекціях? Суть в тому, що різні класи по різному реалізовують роботу з даними. Одні класи швидше здійснюють читання даних, інші вставлення і видалення, одні перевіряють, щоб не було дублювань, інші дозволяють вставляти дані за певним ключем і т.п. Доволі важливо підібрати клас, який найкраще підходить для вашого завдання і забезпечить найбільшу швидкодію. Особливо це актуально, коли кількість об’єктів величезна. Реалізації інтерфейсу Collection Клас ArrayList та використання ітератора Клас ArrayList призначений для читання об'єктів по індексу. Тож не дарма у назві є слово Array (масив). Після створення колекції на основі ArrayList, прочитати дані можна кількома способами. Наступний приклад демонструє створення ArrayList, його наповнення об'єктами типу String та їх читання за допомогою методу get (int index) та за допомогою ітератора. import java.util.ArrayList; import java.util.ListIterator;

public class TestArrayList {

private ArrayList<String> a1;

public static void main(String[] args) { TestArrayList test = new TestArrayList(); test.create(); test.getData(); test.iterateData();

void create() { //створюємо і наповнюємо ArrayList a1 = new ArrayList<String>(); a1.add("Привіт"); a1.add("тобі"); a1.add("божевільний"); a1.add("світе!");

//читаємо дані по індексу void getData() { for (int i = 0; i < a1.size(); i++) { System.out.print(a1.get(i) + " ");

//Читаємо вміст ArrayList з допомогою ітератора void iterateData() { ListIterator<String> it = a1.listIterator(); while (it.hasNext()) { System.out.print(it.next() + " ");

Результат: Привіт тобі божевільний світе! Привіт тобі божевільний світе!

Крім вищенаведених способів можна передати вміст ArrayList у звичайний масив за допомогою методу toArray(). Якщо ви хочете детально розібратися з ArrayList і його методами, то для цього також дивіться інформацію про інтерфейси Collection, List та Iterator.

Окремо розглянемо перегляд даних з допомогою ітератора.

ListIterator<String> it=a1.listIterator();

Таким чином створюється об’єкт ітератора, посилання на який передається об'єктній змінній it типу ListIterator. ListIterator – це інтерфейс, який розширює інтерфейс Iterator декількома новими методами. Базовими ж методами інтерфейсу Iterator є ті, що використані у нас в програмі, а саме:

boolean hasNext() – повертає true, якщо ітерація має наступний елемент E next() - повертає наступний елемент ітерації (буква E вказує, що це може бути елемент будь-якого типу, детальніше див. розділ Узагальнення) void remove() – знищує останній елемент, що повертався ітератором

Тож у коді бачимо: while(it.hasNext()){ System.out.print(it.next()+" "); } Цикл працює поки є елементи в ітераторі. Перевірка здійснюється за допомогою методу hasNext. А вивід елементів здійснюється за допомогою методу next. Перевірка за допомогою hasNext необхідна через те, що в разі відсутності наступного елементу при виклику методу next буде викинуто виняток NoSuchElementExeption.

Інтерфейсом ListIterator передбачено ще такі методи як add, hasPrevious, next, nextIndex, previous, previousIndex, set. Назви методів говорять самі за себе. Детальніше ви можете подивитися в документації по інтерфейсу ListIterator.

Клас Itr, який реалізовує інтерфейс ListIterator є внутрішнім класом класу AbstractList. ArrayList є нащадком класу AbstractList.

Для того, щоб ітератор міг працювати з певним об’єктом, клас даного об’єкту повинен реалізовувати інтерфейс Iterable: public interface Iterable<E> { Iterator<E> iterator(); } Інтерфейс Collection розширює даний інтерфейс.

Можна також перебрати елементи за допомогою перевантаженого з виходом java 5 циклу for (так званого “for each”): for (String str : a1) { System.out.print(str+" "); } При компіляції даний цикл перетворюється компілятором у цикл із ітератором.

ArrayList можна також перетворити у звичайний масив за допомогою методу toArray(): String strArray[]= new String[a1.size()]; strArray=a1.toArray(strArray); System.out.println(strArray);

Якщо ви погано розумієте вищенаведений приклад і як усе працює зверніться до розділів, що описують основні концепції об’єктно-орієнтованого програмування і, зокрема, розберіть детально теми інтерфейсів, абстрактних класів та поліморфізму.

Клас LinkedList LinkedList — це структура даних, що являє собою пов’язаний список елементів (об’єктів).

Різниця між ArrayList та LinkedList полягає в тому, що ArrayList реалізований у вигляді масиву, а LinkedList у вигляді пов’язаних між собою об’єктів. ArrayList швидко виконує читання і заміну елементів (посилань на об’єкти), проте, щоб вставити новий елемнт в середину ArrayList або видалити існуючий в середині ArrayList здійснюється послідовний зсув цілого ряду елементів масиву. В LinkedList доволі швидко відбувається вставлення нового елементу або видалення існуючого. Це відбувається тому, що в середині реалізації LinkedList змінюються лише посилання на попередній і наступний об’єкти (елементи). Проте доступ до об’єктів по індексу в LinkedList відбувається повільніше ніж в ArrayList. Тож загалом, LinkedList корисний, коли необхідно часто вставляти та видаляти елементи зі списку, а в інших випадках краще використовувати ArrayList.

Існує два конструктури LinkedList: LinkedList() LinkedList (Collection c) Перший конструктор створює пустий список, а другий – створює пов’язаний список із іншої колекції.

Клас LinkedList розширює клас AbstractSequentalList та реалізує інтерфейси List, Dequeue та Queue. Реалізація останніх двох інтерфейсів (черг) означає, що ми можемо працювати із пов’язаним списком як із стеком з використанням методів pop(), push(), poll(), pollFirst(), pollLast() і т.п. Детальніше дивіться документацію по заданим інтерфейсам.

Далі попрацюємо з LinkedList, який міститиме в якості елементів об’єктні змінні типу Car.

Спочатку реалізуємо наш клас Car: public class Car { private String name; private double price; private int year; public Car(String name, double price, int year) { this.name = name; this.price = price; this.year=year; } @Override public String toString() { return "\n Car [name=" + name + ", price=" + price + ", year=" + year + "]";

Клас Car містить лише поля, конструктор, який заповнює дані поля та заміщений метод toString(), який видаватиме нам інформацію про автомобіль у вигляді рядка.

Наступний клас демонструє роботу з LinkedList: import java.util.ArrayList; import java.util.LinkedList;

public class TestLinkedList {

private LinkedList<Car> ll=new LinkedList<>();

public static void main(String[] args) { TestLinkedList t=new TestLinkedList(); t.test();

void test(){ Car car1=new Car("Ferrary", 10800, 1995); Car car2=new Car("Запорожець", 2600, 1989); ll.add(car1); ll.add(car2); //додаємо в початок списку ll.addFirst(new Car("Alfa Romeo 155", 11678, 2000)); ll.remove(car2); // видалити об'єкт System.out.println("Після видалення car2: "+ll); ll.remove(1); //видалити елемент за індексом System.out.println("Після видалення першого елементу: "+ll); Car myCar=ll.get(0); System.out.println("Отриманий елемент за індексом :"+myCar); ll.set(0, car1); //замінити елемент за індексом System.out.println("Замінений елемент за індексом "+ll.get(0)); ArrayList<Car> arrList=new ArrayList<Car>(); arrList.add(car1); arrList.add(car2); ll.addAll(arrList); //додаємо вміст ArrayList у наш LinkedList System.out.println("Після додавання ArrayList:"+ll);

Результат виконання програми: Після видалення car2: [ Car [name=Alfa Romeo 155, price=11678.0, year=2000], Car [name=Ferrary, price=10800.0, year=1995]] Після видалення першого елементу: [ Car [name=Alfa Romeo 155, price=11678.0, year=2000]] Отриманий елемент за індексом : Car [name=Alfa Romeo 155, price=11678.0, year=2000] Замінений елемент за індексом Car [name=Ferrary, price=10800.0, year=1995] Після додавання ArrayList:[ Car [name=Ferrary, price=10800.0, year=1995], Car [name=Ferrary, price=10800.0, year=1995], Car [name=Запорожець, price=2600.0, year=1989]] Перебирати елементи LinkedList також можна з допомогою ітератора та у циклі for earch.

Детальніше про методи класу дивіться документацію по класу LinkedList.

Клас HashSet HashSet – це клас призначений для зберігання даних у вигляді множини невпорядкованих елементів. Якщо потрібна впорядкована множина, то використовуйте TreeSet. HashSet також не гарантує стабільного порядку збереження об’єктів. Тобто при додаванні об’єктів порядок зберігання елементів змінюється. Вони можуть бути збережені як в кінці множити так і в середині. Якщо потрібен один порядок зберігання об’єктів використовуйте LinkedHashSet.

Сам термін «множина» означає, що елементи не будуть повторюватися. Для зберігання і пошуку елементів використовується хеш-код об’єкта. HashSet також може містити значення null. Власне всередині самої реалізації HashSet використовується клас HashMap, який дозволяє зберігати елементи у вигляді двох складових ключа та хеш-коду. У класі HashSet хеш-код недоступний і використовується неявно для користувача.

Клас HashSet розширює клас AbstractSet та реалізує інтерфейс Set. Також реалізовує інтерфейси Serializable та Clonable. HashSet має такі конструктори: HashSet() HashSet (Collection c) HashSet (int об’єм) HashSet (int об’єм, float коефіцієнт_заповнення) Коефіцієнт заповнення – це число в межах 0.0 до 1.0, що представляє собою частку заповнення HashSet при якій об’єм HashSet буде збільшений. По замовчуванню в конcтрукторах, що не задають коефіцієнт заповнення, використовується значення 0.75.

Наступний приклад демонструє роботу із HashSet. import java.util.HashSet;

public class TestHashSet {

HashSet<String> hs=new HashSet<String>(); public static void main(String[] args) { TestHashSet test=new TestHashSet(); test.test();

void test(){ hs.add("Австралія"); hs.add("Україна"); hs.add("США"); System.out.println("1) Три країни: "+hs+" розмір="+hs.size()); hs.add("Австралія"); // помилки не буде, але в HashSet нічого не поміняється System.out.println("2) Після спроби додати Авcтралію ще раз: "+hs); hs.remove("США"); //видаляємо США з множини hs.add("Німеччина"); hs.add("Англія"); hs.add(null); hs.add(null); // другий раз не додаситься System.out.println("3)"+hs); System.out.println("4) Чи містить множина Німеччину? "+hs.contains("Німеччина")); System.out.println("5) Множина пуста? "+hs.isEmpty()); //можемо також отримати ітератор, або ж перебрати множину у for earch for (String str:hs){ System.out.println(str);

hs.clear(); // очистити System.out.println("6) Розмір після очищення="+hs.size());

Результат виконання: 1) Три країни: [Австралія, Україна, США] розмір=3 2) Після спроби додати Австралію ще раз: [Австралія, Україна, США] 3)[null, Австралія, Німеччина, Україна, Англія] 4) Чи містить множина Німеччину? true 5) Множина пуста? false null Австралія Німеччина Україна Англія 6) Розмір після очищення=0