Сегодня поговорим о коллекциях – структурах данных для хранения объектов. Самое короткое и простое определение коллекции: коллекция – это объект, который хранит другие объекты.
Для начала выясним: для чего нам нужны коллекции если у нас уже есть массивы, которые могут хранить другие объекты? Все дело в простоте и удобстве использования. Зачем изобретать велосипед, если его уже изобрели до нас. Мы просто берем и используем готовые решения, которые зачастую будут работать быстрее наших. Когда мы познакомимся с разными коллекциями, Вы поймете, что некоторые из них и строятся на массивах.
В чем еще преимущество использования коллекций:
- удобство тестирования кода;
- структуры данных на любой вкус и потребность;
- повторное использование кода.
И это еще не полный список. По мере того, как Вы будете программировать, Вы сможете по настоящему оценить коллекции в java.
В Java есть два главных интерфейса от которых и наследуются все остальные классы коллекций: Collection, Map.
Collection – хранит набор объектов в виде к которому мы уже привыкли изучая массивы: есть объект он помещается в ячейку. С ним возможны все манипуляции: удаление, вставка нового, поиск и т.д.
Map – хранит данные в виде пары “ключ-значение”. Сегодня мы поговорим только о первом интерфейсе в связи с тем, что тема достаточно обширная и важная.
Интерфейс Collection наследуется другими интерфейсами, которые в свою очередь имплементируются классами, в которых реализована та или иная структура данных.
На картинке выше Вы можете посмотреть иерархию интерфейсов коллекции и увидеть методы, которые у них есть. Как видим методов достаточно много и они очень полезные в работе с массивами данных.
От этих интерфейсов, как я уже говорил выше идет реализация классов.
Есть еще абстрактные классы, от которых унаследуются классы выше, но я сознательно упускаю от Вас эту информацию поскольку считаю, что это только усложнит и так запутанную структуру классов коллекций. Она сложна только на первый взгляд. Разобравшись в тонкостях, Вы уже сможете найти более развернутую информацию и нюансы об этих структурах данных.
ArrayList – коллекция на основе массива. Имеет свойство изменять свой размер в зависимости от того удаляются или добавляются элементы.
LinkedList – коллекция на основе связанного списка. Элементы помещенные в данную коллекцию сохраняют свой порядок вставки. То есть, в каком порядке был вставлен элемент, в таком порядке он будет при выводе. Каждый элемент, который хранится в LinkedList, содержит ссылки на «соседей». Это упрощает добавление и удаление элементов в списке. Классы ArrayList и Vector предпочтительнее использовать для поиска элементов, потому что эти классы используют индексы для доступа к элементам. Однако вставка и удаление элементов для них будет медленнее, чем LinkedList.
Vector – тот же самый ArrayList с той разницей, что методы данной коллекции синхронизованы. То есть, потокобезобасны.
Stack – список, который реализует данные стека. Элементы размещаются по принципу LIFO (last-in, first-out) – последний пришел, первым ушел.
Теперь немного практики. Здесь, я не буду приводить много методов и все структуры данных, так как они интуитивно понятны и очень простые в использовании.
import java.util.ArrayList;
import java.util.List;
public class ListExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();//создание нового списка
stringList.add("Ivan");//добавление элементов
stringList.add("Elena");
stringList.add("Sergey");
System.out.println(stringList.size());//размер списка
stringList.get(1);//получения элемента по индексу (начинается с 0 элемента)
boolean isEmpty = stringList.isEmpty();//проверка списка на пустоту
System.out.println(isEmpty);
System.out.println("Просто вывод элементов");
stringList.stream().forEach(s-> System.out.println(s));//очень полезный метод стрим
stringList.stream().filter(s-> s.startsWith("I")).map(String::toLowerCase).forEach(System.out::println);
}
}
Результат работы программы:
false
Просто вывод элементов
Ivan
Elena
Sergey
ivan
Хотелось бы обратить внимание на метод stream, который является нововведением в Java 8. Если интересно, можно почитать отдельную статью о стрим апи.
Заменив ArrayList на LinkedList мы получаем тот же набор методов для этой структуры данных. Выбор в использовании структур зависит от требований программы. Преимущества каждой структуры мы описали выше. Что еще отличает LinkedList от ArrayList так это то, что на выходе Вы получите элементы в том же порядке, в котором Вы их добавляли в LinkedList. В то время, как ArrayList не гарантирует это. Те же самые методы будут работать для Vector и Stack.
Теперь перейдем к Set.
Set – коллекция, которая не содержит повторяющихся элементов.
HashSet – набор, в котором элементы хранятся в хеш-таблице. У элементов нет строгого порядка. HashSet использует метод hashCode своих элементов для определения их размещения в наборе.
LinkedHashSet – элементы хранятся в виде связанного списка. Только элементы хранятся в сортированном виде.
TreeSet – хранит элементы в структуре данных дерева, которая также сортируется и доступна для навигации. Методы добавление, удаление и получить элемент, гарантируют работу в log (n) времени, где n – количество элементов в дереве.
import java.util.Set;
import java.util.TreeSet;
public class SetExample {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<>();
set.add(20);
set.add(15);
set.add(30);
set.add(30);
set.add(30);
System.out.println(set);
set.remove(20);
System.out.println(set);
}
}
Результат работы программы:
[15, 20, 30]
[15, 30]
Как видим, добавление еще одной 30 не удалось, что подтверждает теорию о том, что в сет можно добавить только уникальные объекты. Методы данной структуры такие же как и List. Поэтому останавливаться здесь подробно не будем.
Еще предлагаю рассмотреть очередь (Queue).
Queue – коллекция, которая хранит элементы в определенном порядке, нужном для их обработки. Помимо основных методов Collection в этом интерфейсе добавлены дополнительные методы вставки, проверки, извлечения. В данной коллекции элементы обычно размещаются в порядке FIFO (first-in, first-out) – первым пришел, первым ушел.
Теперь к реализациям.
PriorityQueue – очередь, в которой элементы упорядочиваются на основании заданного вами параметра(в отличие от параметра на основе FIFO). Эта очередь упорядочивает элементы либо по их натуральному порядку (используя интерфейс Comparable), либо с помощью интерфейса Comparator, полученному в конструкторе.
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
public class QueueExample {
public static void main(String[] args) {
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer comp1, Integer comp2) {//настроили метод таким образом, что элементы будут добавляться в обратном порядке
if( comp1 > comp2 ){
return -1;
}
if( comp1 < comp2 ){
return 1;
}
return 0;
}
};
Queue<Integer> priorityQueue = new PriorityQueue<>(10, comparator);
priorityQueue.add(7);
priorityQueue.add(4);
priorityQueue.add(9);
priorityQueue.add(1);
priorityQueue.add(80);
System.out.println(priorityQueue);
}
}
Результат работы программы: [80, 9, 1, 7]
Как я уже говорил выше, Qeue имеет свои собственные методы получения, вставки и удаления элемента. Отличия от стандартных методов Collection заключаются в том, что если стандартные методы генерируют исключения, то методы Queue возвращают специальное значение.
import java.util.PriorityQueue;
import java.util.Queue;
public class QueueExample {
public static void main(String[] args) {
Queue<Integer> priorityQueue = new PriorityQueue<>();
priorityQueue.add(7);
priorityQueue.add(4);
priorityQueue.add(9);
priorityQueue.add(1);
priorityQueue.offer(80);//добавление
System.out.println(priorityQueue);
System.out.println(priorityQueue.poll());//удаление сверху
System.out.println(priorityQueue.peek());//получить верхний
System.out.println(priorityQueue);
}
}
Результат работы:
[1, 4, 9, 7, 80]
1
4
[4, 7, 9, 80]
Пожалуй, на этом и закончим данную статью. О Map мы поговорим в другой раз. А сейчас, у Вас и так хватит работы, чтобы потренироваться в использовании прочитанного материала. Используйте коллекции, не бойтесь выхода за пределы как это было с массивами. Не следите за размерностью – об этом уже позаботились разработчики коллекций.