Интерфейс Set в Java

Благодаря классам реализующим Set можно создать массив в котором элементы не повторяются.

HashSet это как hashmap, только без значений, только ключи.

В hashset могут храниться только уникальные ключи, благодаря уже ранее упомянутому hashcode, который есть у каждого элемента добавляемого в hashset.

Поясним на примере:

import java.util.*; public class Set { public static void main(String[] args) { // создаем коллекцию HashSet hSet = new HashSet(); // добавление элемента hSet.add(“Sunday”); hSet.add(“Monday”); hSet.add(“Sunday”); hSet.add(“Tuesday”); // удаление элемента hSet.remove(“Monday”); // в hashset нету get поэтому сначала нужно преобразовать // в массив а потом извлекать либо использовать iterator String[] hSetArr = hSet.toArray(new String[hSet.size()]); for(int i = 0; i < hSetArr.length; i++) { System.out.println(hSetArr[i]); } // проверка на наличие элемента в списке System.out.println(hSet.contains("Tuesday")); // Как можно увидеть по результатам в массиве всего один Sunday } }

Вывод:


TreeSet

TreeSet – хранит элементы в виде дерева, что позволяет быстрее искать элементы.

Все элементы отсортированы и не повторяются.

TreeSet лучше всего подходит для нахождения диапазонов.

Пример программы:

import java.util.*; public class Set { public static void main(String[] args) { // создаем коллекцию TreeSet tSet = new TreeSet(); // добавление элемента tSet.add(“D”); tSet.add(“B”); tSet.add(“A”); tSet.add(“C”); tSet.add(“E”); // удаление элемента tSet.remove(“C”); // В TreeSet нету get поэтому сначала нужно преобразовать // в массив а потом извлекать либо использовать iterator String[] tSetArr = tSet.toArray(new String[tSet.size()]); for(int i = 0; i < tSetArr.length; i++) { System.out.println(tSetArr[i]); } // проверка на наличие элемента в списке System.out.println(tSet.contains("П")); // для нахождения диапазона System.out.println(tSet.tailSet("B")); System.out.println(tSet.headSet("B")); // Как можно увидеть по результатам в массиве // всего один В и элементы хранятся в алфавитном порядке } }

Вывод:


LinkedHashSet

LinkedHashSet – хранит элементы в порядке вставки и элементы не повторяются.

Опять таки, лучше использовать когда элементы часто вставляются/удаляются.

Пример программы:

import java.util.*; public class Set { public static void main(String[] args) { // создаем коллекцию LinkedHashSet ltSet = new LinkedHashSet(); // добавление элемента ltSet.add(“D”); ltSet.add(“B”); ltSet.add(“A”); ltSet.add(“C”); ltSet.add(“B”); // удаление элемента ltSet.remove(“C”); // LinkedHashSet нету get поэтому сначала нужно преобразовать // в массив а потом извлекать либо использовать iterator String[] ltSetArr = ltSet.toArray(new String[ltSet.size()]); for(int i = 0; i< ltSetArr.length; i++) { System.out.println(ltSetArr[i]); } // проверка на наличие элемента в списке System.out.println(ltSet.contains("B")); //Как можно увидеть по результатам в масиве всего один В // элементы хранятся в порядке вставки } }

Вывод:

Интерфейс Map в Java

Благодаря классам реализующим Map можно создать массив из пар ключ-значение. То есть один элемент этого массива это пара.

Пример программы:

import java.util.*; public class Map { public static void main(String[] args) { //создаем коллекцию HashMap< Integer, String > hMap = new HashMap< Integer, String >(); //добавление элемента //видим числовой ключ и соответствующее ему значение hMap.put(34, “Sunday”); hMap.put(56, “Monday”); hMap.put(85, “Tuesday”); hMap.put(34, “Wednesday”); hMap.put(556, “Monday”); hMap.put(724, “Sunday”); // удаление элемента hMap.remove(85); //В hashset нету get по индексу и не наследует iterator //поэтому передаем значения с помощью метода values //в конструктор другой коллекции, которую можно перебирать. ArrayList< String > hMapValues = new ArrayList< String >(hMap.values()); for(int i = 0; i < hMapValues.size(); i++) { System.out.print(hMapValues.get(i) + " "); } System.out.println(); //или keySet если нужно перебирать ключи ArrayList< Integer > hMapKeys = new ArrayList< Integer >(hMap.keySet()); for(int i = 0; i < hMapKeys.size(); i++) { System.out.print(hMapKeys.get(i) + " "); } System.out.println(); // проверка на наличие элемента с таким ключем в списке System.out.println(hMap.containsKey(56)); // проверка на наличие элемента с таким значением в списке System.out.println(hMap.containsValue("Sunday")); // можно также получить значение элемента hashmap по ключу System.out.println(hMap.get(34)); } }

Вывод:


Hash коллекции внутри

Search Icon

Довольно важно знать как Hash коллекции работают под капотом.

HashMap под капотом разбит на 16 linkedlist в которых собственно и хранятся пары. То есть сам HashMap это тоже массив из 16 ячеек, но каждая ячейка представляет собой отдельный LinkedList.

Добавление элемента в HashMap происходит так, что сначала по ключу вычисляется некий код, который называется hashcode, а по нему индекс нужного LinkedList-а в массиве из 16 LinkedList и в linkedlist-е с этим индексом сохраняется элемент.

При выборке из HashMap, например с помощью get, ранее вставленного элемента, будет происходить тот же процесс, что и при его вставке, то есть расчет hashcode по ключу, который был передан в get и собственно выборка из того linkedlist-а который соответствует рассчитанному hashcode.

Example

Например:

Очевидно, что и при вставке, например, ключа 10, и при выборке по этому ключу будет рассчитан один и тот же hashcode. Соответственно и вставка ключа 10 и выборка по ключу 10 будет происходить в одном и том же linkedlist-е.

Для ключа, например 13, вставка и выборка уже, возможно, попадет на другой linkedlist. А возможно попадет на тот linkedlist где перед перед этим вставилось 10. Зависит от рассчитанного hashcode для числа 13.

Отсюда очевиден смысл hashmap, что поскольку мы и вставляем и выбираем в одном и том же linkedlist-e, то выборка ясное дело всегда идет в одном из 16 linkedList-ов, а не во всех, и это ясное дело лучше, чем если бы мы вибирали из всех шестнадцати как в одном большом linkedlist-e.

Search Icon

Также важно упомянуть, что поиск нужного linkedlist-a из 16 по hashcode происходит за константное время, проще говоря скорость лучше не бывает.


TreeMap

TreeMap – хранит пары в виде дерева, что позволяет быстрее искать элементы.

Все элементы отсортированы по ключу.

TreeMap лучше всего подходит для нахождения диапазонов

Пример программы:

import java.util.*; public class Map { public static void main(String[] args) { // создаем коллекцию TreeMap< Integer, String > tMap = new TreeMap< Integer, String >(); // добавление элемента tMap.put(556, “Monday”); tMap.put(56, “Monday”); tMap.put(85, “Tuesday”); tMap.put(34, “Wednesday”); tMap.put(815, “Saturday”); tMap.put(724, “Sunday”); // удаление элемента tMap.remove(85); // в TreeMap нету get по индексу и не наследует iterator // поэтому передаем значения с помощью метода values // конструктор другой коллекции, которую можно перебирать ArrayList< String > tMapValues = new ArrayList< String >(tMap.values()); for(int i = 0; i < tMapValues.size(); i++) { System.out.print(tMapValues.get(i) + " "); } System.out.println(); // или keySet если нужно перебирать ключи ArrayList< Integer > tMapKeys = new ArrayList< Integer >(tMap.keySet()); for(int i = 0; i < tMapKeys.size(); i++) { System.out.print(tMapKeys.get(i) + " "); } System.out.println(); // проверка на наличие элемента с таким ключем в списке System.out.println(tMap.containsKey(56)); // проверка на наличие элемента с таким значением в списке System.out.println(tMap.containsValue("Sunday")); // для нахождения диапазонов System.out.println(tMap.tailMap(556)); // выведет все элементы после 556 System.out.println(tMap.headMap(815)); // выведет все элементы до 815 // по результатам можно увидеть что элементы в порядке увеличения } }

Вывод:


LinkedHashMap

LinkedHashMap – элементы в отличие от hashmap содержат ссылку на предыдущий добавленный элементы и на следующий добавленный, как у LinkedList.

Проще говоря, это гибрид HashMap и LinkedList.

То есть лучше использовать когда элементы часто вставляются/удаляются.

Пример программы:

import java.util.*; public class Map { public static void main(String[] args) { // создаем коллекцию LinkedHashMap< Integer, String > lhMap = new LinkedHashMap< Integer, String >(); // добавление элемента lhMap.put(556, “Monday”); lhMap.put(556, “Monday”); lhMap.put(85, “Tuesday”); lhMap.put(34, “Wednesday”); lhMap.put(815, “Saturday”); lhMap.put(724, “Sunday”); // удаление элемента lhMap.remove(85); //в linkedhashset нету get по индексу и не наследует iterator //поэтому передаем значения с помощью метода values //в конструктор другой коллекции, которую можно перебрать ArrayList< String > lhMapValues = new ArrayList< String >(lhMap.values()); for(int i = 0; i < lhMapValues.size(); i++) { System.out.print(lhMapValues.get(i)+" "); } System.out.println(); //или keySet если нужно перебрать ключи ArrayList< Integer > lhMapKeys = new ArrayList< Integer >(lhMap.keySet()); for(int i = 0; i < lhMapKeys.size(); i++) { System.out.print(lhMapKeys.get(i)+" "); } System.out.println(); // проверка на наличие элемента с таким ключем в списке System.out.println(lhMap.containsKey(556)); // проверка на наличие элемента с таким значением в списке System.out.println(lhMap.containsValue("Sunday")); // По результатам можно видеть что элементы в порядке добавления } }

Вывод:

Интерфейс Queue в Java

Благодаря классам реализующим Queue можно создавать массивы реализующие очередь.

Example

Что такое очередь?

Очередь работает по принципу FIFO – first in first out (первый пришел – первым ушел). Представьте массив в который элементы можно добавлять только по очереди в конец, то есть после самого последнего элемента массива который был туда добавлен, а удалять элементы можно только по очереди начиная с самого первого элемента массива который мы туда добавили. То есть как видим первый вошедший в массив будет удален оттуда первым. Это и есть очередь.

Правда мы уже рассматривали LinkedList, который тоже реализует очередь (FIFO).

Помним, что если у LinkedList вызвать метод add, то элемент добавиться в конец LinkedList, если же вызвать просто remove без аргументов, то удалиться элемент, который был добавлен в очередь наиболее давно.

То есть, очевидно, что это реализация очереди.

У ArrayList, например, нельзя вызвать remove без аргументов, поэтому ArrayList не является реализацией очереди.

У Queue же есть более интересные реализации очереди, например, приоритетная очередьPriorityQueue.

При добавлении элемента в конец массива элементы в нем еще и сортируются по приоритету.

Приоритет задан по умолчанию у некоторых типов.

То есть если, например, массив PriorityQueue хранит Integer числа, то при добавлении числа в него будет происходить сортировка массива так, чтобы все элементы массива располагались в массиве от большего числа к меньшему.

При выборке же элемента из приоритетной очереди, будет происходить выборка элементов с того конца массива, где наименьшее число.


PriorityQueue

Пример программы:

import java.util.*; public class Queue { public static void main(String[] args) { // Создаем приоритетную очередь, в которой // будут храниться числа типа Integer PriorityQueue< Integer > pQueue = new PriorityQueue< Integer >(); // добавление элемента pQueue.add(4); pQueue.add(3); pQueue.add(7); pQueue.add(1); pQueue.add(8); // В PriorityQueue нет метода get, поэтому сначала нужно // преобразовать в массив, а потом извлекать, // либо использовать итератор, который рассмотрим далее Integer[] hSetArr = pQueue.toArray(new Integer[pQueue.size()]); for (int i = 0; i < hSetArr.length; i++) { System.out.println(hSetArr[i]); } // Проверка на наличие элемента в списке System.out.println(pQueue.contains(3)); // Можно увидеть приоритет удаления (от меньшего числа к большему) System.out.println(pQueue.remove()); System.out.println(pQueue.remove()); System.out.println(pQueue.remove()); System.out.println(pQueue.remove()); System.out.println(pQueue.remove()); // Как можно увидеть по результатам, // удаляется от меньшего числа к большему. } }

Вывод:

Как можно увидеть по результатам, удаление происходит от меньшего числа к большему.

Iterator в коллекциях Java

Благодаря Iterator можно перебирать любую коллекцию не вникая в особенности какой-либо из них.

Пример программы:

import java.util.*; @SuppressWarnings(“unchecked”) public class IteratorExample { public static void main(String[] args) { ArrayList aList = new ArrayList(); aList.add(“Sunday”); // добавление элемента aList.add(“Monday”); aList.add(“Tuesday”); // Доступ к каждому элементу через iterator Iterator ir = aList.iterator(); while (ir.hasNext()) { System.out.println(ir.next()); } // То есть у любой коллекции есть iterator. // С помощью которого можно пройтись // по ее элементам. } }

Вывод:

Таким образом, у любой коллекции есть iterator, который позволяет перебирать её элементы.

Коллекции в Java. Интерфейс List

Что же такое коллекция?

Это можно сказать такой особый тип массива.

Мы знаем, что у обычного Java массива есть строгая граница количества элементов, которые могут в нем быть (эту границу мы задаем при инициализации вот так – int [] a = new int[5]).

Это называется статический массив.

Но часто может пригодиться динамический массив, то есть который может расширяться.

То есть у него нет фиксированно возможного количества элементов.

В Java в библиотеке Collections есть много разных динамических массивов каждый из которых нужен для разных задач и ситуаций.

То есть они не просто динамические массивы, а еще и имеют разную структуру и применение.

Поэтому в Java корректно называть их не просто “динамическими массивами”, а коллекциями.

Есть четыре основных вида коллекций List, Queue, Set и Map.

Каждая из коллекций имеет свои подвиды, которые мы будем разбирать.

Важно упомянуть что List, Queue, Set и Map это интерфейсы которые имеют разные реализации (подвиды).


ArrayList

Для начала разберем реализации List.

Благодаря классам реализующим List можно создать динамически изменяющийся массив.

Первый подвид List, который мы рассмотрим это ArrayList.

ArrayList – лучше использовать когда часто нужен доступ по индексу

Пример программы:

import java.util.*; public class ListLesson { public static void main(String[] args) { //Создаем список. //Как видим никакого размера не указываем ArrayList aList = new ArrayList(); // добавление элемента aList.add(“Sunday”); aList.add(“Monday”); aList.add(“Tuesday”); // удаление элемента aList.remove(“Monday”); //Доступ к каждому элементу с помощью get. //метод get для доступа к элементам по индексу for(int i = 0; i< aList.size(); i++) { System.out.println(aList.get(i)); } // проверка на наличие элемента в списке System.out.println(aList.contains("Tuesday")); } }

Вывод:


LinkedList

LinkedList – лучше использовать когда часто производиться вставка/удаление из массива, занимает больше памяти чем ArrayList.

Пример программы:

import java.util.*; public class ListLesson { public static void main(String[] args) { // Создать новый объект LinkedList LinkedList llist = new LinkedList(); // Добавление элементов в связанный список // тем же методом что и у ArrayList. llist.add(“Days in a Week”); // добавить // Добавить в произвольное место связанного списка llist.add(1, “Middle”); llist.add(2, “End”); // Удаление элемента llist.remove(“End”); // Если не вписать аргумент, то удалится элемент, // который был добавлен в массив наиболее давно. llist.remove(); // Доступ к каждому элементу с помощью get for (int i = 0; i < llist.size(); i++) { System.out.println(llist.get(i)); } // проверка на наличие элемента в списке System.out.println(llist.contains("Tuesday")); } }

Вывод:

Из реализаций List есть еще Vector – это как ArrayList, только синхронизирован(для работы с потоками, их будем проходить потом).


Конвертация коллекции из одного типа в другой.

Можно при создании коллекции поместить в нее элементы из другой ранее созданной коллекции.

При этом эта другая коллекция может быть другого типа по отношению к создаваемой. То есть фактически это значит, что возможно менять тип коллекции.

Пример программы:

import java.util.*; class ListLesson { public static void main(String[] args) { // создаем список ArrayList aList = new ArrayList(); aList.add(“Sunday”); aList.add(“Monday”); aList.add(“Tuesday”); // Создать новый объект LinkedList LinkedList lList = new LinkedList(); lList.add(“Days in a Week”); lList.add(1, “Middle”); lList.add(2, “End”); //Можно создать коллекцию с элементами другой коллекции //другого типа. Можно сказать, что снизу мы поменяли тип //ранее созданной коллекции lList с LinkedList на ArrayList. ArrayList< String > LinkedToArray = new ArrayList<>(lList); LinkedList< String > ArrayToLinked = new LinkedList<>(aList); //Выведем на консоль коллекции for(int i = 0; i < LinkedToArray.size(); i++) { System.out.println(LinkedToArray.get(i)); } for(int i = 0; i< ArrayToLinked.size(); i++) { System.out.println(ArrayToLinked.get(i)); } //также можно задать значение //начального размера внутреннего массива ArrayList< String > list2 = new ArrayList<>(10000); } }

Вывод: