Эта статья, как и все последующие и предыдущие – моя попытка структурировать полученные знания в процессе изучения Java. Здесь тезисно собрана вся основная информация по теме и те формулировки, которые показались мне наиболее удачными и понятными. Это мой конспект, если хотите.
Статья будет полезна тем, кто изучает или повторяет основы Java Core.
И тем, кто готовится к техническому интервью.
На источники, откуда черпалась информация, предоставлены ссылки в конце статьи.
Оглавление
Основные понятия
Структура данных (англ. Data Structure) — программная единица, позволяющая хранить и обрабатывать однотипные и/или логически связанные данные.
Для добавления, поиска, изменения и удаления данных структура данных предоставляет некоторый набор функций, составляющих её интерфейс.
Структура данных – это контейнер, который хранит данные в организованной форме.
Массив – самая простая и наиболее широко используемая структура.
Коллекции Java Сollections Framework являются производными от массивов.
Java Collections Framework — это набор связанных классов и интерфейсов, реализующих широко используемые структуры данных — Коллекции.
Он был спроектирован и разработан, в первую очередь, Джошуа Блохом.
Массивы
Массив – это структура данных, состоящая из упорядоченных ячеек, расположенных друг за другом в памяти, которые могут хранить в себе элементы только одного, заранее заданного типа.
Может хранить данные примитивных типов, строки (String) и ссылки на объекты класса.
Размер массива задается при его создании.
Тип данных и длину массива в дальнейшем изменить нельзя.
int[] numbers = new int[100]; // массив чисел на 100 ячеек
String[] strings = new String[10]; // массив строк на 10 ячеек
Person[] people = new Person[5]; // массив объектов класса Person
Элементы массива доступны по индексу ячейки.
Значение по умолчанию для ячеек – null
Отсчет индексов ведется от 0
В памяти элементы массива размещаются в едином блоке.
Это сделано для более эффективного и быстрого доступа к ним.
String[] strings = new String[10];
strings[0] = "Hello"; // присвоить значение первой ячейке
strings[1] = " world!"; // присвоить значение второй ячейке
System.out.println(strings[0] + strings[1]); // получить значения по индексу
Быстрая инициализация массива
При быстрой инициализации размер массива не задается – размер будет определен автоматически в соответствии с количеством переданных в массив элементов.
int[] numbers = new int[]{1, 2, 3, 4, 5};
int[] numbers = {1, 2, 3, 4, 5}; // без ключевого слова new
String[] strings = {"Hello", " world!"};
Копирование массива System.arraycopy()
Этот статический метод класса System создан специально для копирования массива.
Копирует массив из указанного исходного массива, начиная с указанной позиции, в указанную позицию целевого массива.
System.arraycopy(array1, 0, array2, 0, array1.length);
Массивы бывают двух типов:
Одномерные
Массив хранит однотипные элементыМногомерные
Массив хранит ссылки на другие массивы
int[][] numbers = new int[3][]; // двумерный массив
String[][][] threeDimArray = new String[3][][]; // трехмерный массив
String[][] strings = new String[3][5]; // таблица 3 столбца 5 строк
Инициализация двумерного массива
Обязательно задать размер только первого массива.
Каждый из внутренних массивов может быть своей длины – указывается при добавлении.
int[][] numbers = new int[3][];
numbers[0] = new int[2];
numbers[1] = new int[4];
numbers[2] = new int[3];
Быстрая инициализация двухмерного массива
int[][] numbers = {{1, 2}, {1, 2, 3}, {1, 2, 3, 4, 5}};
Добавить элемент в двухмерный массив
Сначала указывается индекс ячейки многомерного массива int[][] numbers
, а затем индекс ячейки внутреннего массива
int[][] numbers = new int[3][3];
numbers[0][0] = 1;
numbers[1][0] = 2;
numbers[2][0] = 3;
Класс Arrays
Класс Arrays содержит различные статические методы для работы с Массивами (например, сортировка и поиск).
public final class Array extends Object
Сортировка массива Arrays.sort()
Отсортировать можно как весь массив, так его отдельную часть – диапазон индексов.
int[] numbers = {3, 2, 5, 1, 4};
Arrays.sort(numbers); // [1, 2, 3, 4, 5]
Arrays.sort(numbers, 0, 4); // [1, 2, 3, 5, 4]
Привести содержимое массива к строке Arrays.toString()
Для строкового представления многомерного массива создан отдельный метод Arrays.deepToString()
String str = Arrays.toString(strings);
String str = Arrays.deepToString(numbers); // для многомерных массивов
Сравнить два массива между собой Arrays.equals()
Для сравнения содержимого массивов, а не ссылок, класс Arrays содержит переопределенный метод equals()
Для сравнения многомерных массивов используем метод Arrays.deepEquals()
Arrays.equals(numbers, numbers2);
Arrays.deepEquals(numbers, numbers2) // для многомерных массивов
Заполнить массив одинаковыми значениями Arrays.fill()
Заполнить можно как весь массив, так и его часть – диапазон индексов.
Последний индекс не входит в диапазон.
Работает только с одномерными массивами.
Arrays.fill(numbers, 777); // присвоить всем ячейкам значение 777
Arrays.fill(numbers, 3, 10, 777); // заполнить ячейки с 3 по 9 индекс
Перевести Массив в List Arrays.asList()
List<Integer> listNumbers = Arrays.asList(arrayNumbers);
List<Integer> listNumbers = Arrays.asList(1, 2, 3);
Копирование массива Arrays.copyOf()
, Arrays.copyOfRange()
Если элементы не поместились в новый массив – лишние значения игнорируются.
Если длина нового массива больше – ячейки заполняются значениями по умолчанию.
Под капотом используется метод System.arraycopy()
int[] numbersCopy = Arrays.copyOf(numbers, numbers.length);
int[] numbersCopy = Arrays.copyOf(numbers, 4); // начать с индекса 4
int[] numbersCopy = Arrays.copyOfRange(numbers, 2, 7); // индекс 7 НЕ входит
Бинарный поиск Arrays.binarySearch()
Для этого массив предварительного нужно отсортировать Arrays.sort()
Двоичный (бинарный) поиск (также известен как метод деления пополам или дихотомия) — классический алгоритм поиска элемента в отсортированном массиве (векторе), использующий дробление массива на половины.
Возвращает индекс искомого элемента в массиве либо отрицательное число – точку вставки (-(insertion point) - 1)
Точка вставки определяется как точка, в которую ключ будет вставлен в массив.
int[] numbers = {3, 2, 5, 1, 4};
Arrays.sort(numbers); // [1, 2, 3, 4, 5]
Arrays.binarySearch(numbers, 4); // вернет индекс ячейки 3
Arrays.binarySearch(numbers, 6); // вернет точку вставки -5 - 1 = -6
Collections Framework. Коллекции
Главным ограничением массива является его фиксированная длина.
Эту проблему решают Коллекции – набор интерфейсов и классов для работы с группами объектов.
Коллекции строго структурированы.
В основе любой коллекции лежит использование того или иного интерфейса, который определяет ее базовый функционал.
Два главных интерфейса коллекций – Collection и Map
От них наследуют все другие интерфейсы и классы Collection Framework.
Интерфейс Collection наследует интерфейс Iterable, который позволяет последовательно обходить элементы.
public interface Collection<E> extends Iterable<E>
public interface Map<K,V>
Collection хранит объекты как в массиве: каждый помещается в ячейку по индексу.
В отличие от массивов, не может хранить примитивные типы.
Map – хранит данные в виде пары ключ – значение.
Map не наследуется от интерфейса Collection, но входит в состав Collections Framework.
Иерархия Collections Framework
Интерфейс Iterable. Iterator. ListIterator
Все коллекции Collection наследуют интерфейс Iterable .
Метод этого интерфейса iterator()
возвращает Итератор над элементами коллекции.
List<String> list = new ArrayList();
Iterator<String> iterator = list.iterator();
Интерфейс Iterator – это поведенческий шаблон проектирования.
Дает возможность последовательно обходить элементы коллекций.
Позволяет перемещаться по коллекции, получать или удалять элементы.
Одну и ту же коллекцию могут одновременно обходить различные итераторы.
Методы Iterator
|
Выполняет данное действие для каждого оставшегося элемента до тех пор, пока все элементы не будут обработаны или действие не выдаст исключение. |
|
Возвращает |
|
Возвращает следующий элемент в итерации. |
|
Удаляет из базовой коллекции последний элемент, возвращенный этим итератором. |
Нельзя одновременно производить итерацию коллекции и удаление ее элементов.
Чтобы удалить элемент - его сначала нужно получить.
while (iterator.hasNext()) {
Integer next = iterator.next();
iterator.remove();
iterator.forEachRemaining(System.out::println);
}
Интерфейс ListIterator
Расширяет Iterator, обеспечивает двунаправленный обход списка List и модификацию элементов. Позволяет обойти коллекцию в обоих направлениях.
Доступен только для коллекций, которые реализуют List – получаем его с помощью метода интерфейса List listIterator()
List<String> list = Arrays.asList("A", "B", "C", "D", "E");
ListIterator<String> listIterator = list.listIterator();
Методы ListIterator
|
Вставляет указанный элемент в список перед элементом, который должен быть возвращен следующим вызовом |
|
Возвращает |
|
Возвращает |
|
Возвращает следующий элемент в списке и перемещает положение курсора. |
|
Возвращает индекс элемента, который будет возвращен следующим вызовом метода |
|
Возвращает предыдущий элемент в списке и перемещает положение курсора назад. |
|
Возвращает индекс элемента, который будет возвращен следующим вызовом метода |
|
Удаляет из списка последний элемент, который был возвращен |
|
Заменяет последний элемент, возвращаемый методом |
Изначально курсор итератора находится в начале списка и для того, чтобы сделать итерацию назад – нужно сделать хоть одну итерацию вперед.
Без этого вызов метода hasPrevious()
будет возвращать false
while (listIterator.hasNext()) {
String element = listIterator.next();
listIterator.set(element + ",");
}
while (listIterator.hasPrevious()) {
String element = listIterator.previous();
System.out.print(element + " ");
}
Интерфейс Collection
Интерфейс Collection расширяет интерфейс Iterable – благодаря этому все классы наследники Collection можно перебирать Итератором.
public interface Collection<E> extends Iterable<E>
Этот интерфейс содержит основные методы, для работы с коллекциями.
Их получают все коллекции (за исключением Map).
Поэтому общие принципы работы с коллекциями будут одни и те же.
Основные методы Collection:
|
Добавление элемента в коллекцию. |
|
Добавляет все элементы указанной коллекции в эту коллекцию. |
|
Удаляет все элементы из этой коллекции |
|
Возвращает |
|
Возвращает |
|
Возвращает |
|
Удаляет один экземпляр указанного элемента из этой коллекции, если он присутствует. |
|
Удаляет все элементы этой коллекции, которые содержатся в указанной коллекции. |
|
Сохраняет только те элементы в этой коллекции, которые содержатся в указанной коллекции. |
|
Возвращает количество элементов в этой коллекции. |
|
Возвращает последовательный поток с этой коллекцией в качестве источника. |
|
Возвращает массив, содержащий все элементы этой коллекции. |
Интерфейс List
Интерфейс List расширяет Collection.
Используется для создания простых списков.
Элементы списка упорядочены и могут быть доступны по индексу.
Список может содержать повторяющиеся элементы.
public interface List<E> extends Collection<E>
Помимо Iterator списки также могут вернуть ListIterator, который позволяет вставку и замену элементов, а также двунаправленный доступ.
Основные методы List:
|
Добавляет элемент в конец этого списка. |
|
Добавляет элемент по индексу в этот список. Старые элементы сдвигаются вправо и их индекс увеличивается на 1. |
|
Вставляет все элементы указанной коллекции в список с указанного индекса. |
|
Возвращает элемент списка по указанному индексу. |
|
Заменяет элемент по индексу в этом списке на указанный элемент. |
|
Возвращает индекс первого указанного элемента в списке или -1, если элемент отсутствует. |
|
Возвращает индекс последнего элемента в этом списке или -1, если элемент отсутствует. |
|
Возвращает итератор списка по элементам этого списка. |
|
Возвращает итератор списка по элементам этого списка, начиная с указанного индекса. |
|
Возвращает часть коллекции от позиции |
|
Возвращает массив, содержащий все элементы этого списка. |
Класс ArrayList
ArrayList сохраняет элементы во внутренний массив и управляет его расширением.
По мере добавления элементов емкость внутреннего массива автоматически увеличивается.
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
List создается с начальной емкостью capacity
10 ячеек
Переменная size
хранит количество добавленных элементов и изначально равна 0
size
– это не емкость внутреннего массива – емкость массива нам недоступна.
Конструкторы ArrayList
List<String> list = new ArrayList();
List<String> list2 = new ArrayList(100); // задаем емкость capacity
List<String> list3 = new ArrayList<>(list);
list.add(" world!"); // добавить в конец списка
list.add(0, "Hello"); // добавить по индексу
list.size(); // сколько добавлено элементов
list.remove("Hello"); // удалить по значению
list.remove(1); // удалить по индексу
ArrayList автоматически расширяется по формуле: (10 * 1,5) + 1
Автоматически внутренний массив не уменьшается.
Чтобы обрезать емкость списка до реального количества элементов в нем – используем метод trimToSize()
Этот метод есть только у ArrayList и отсутсвует у List.
ArrayList list = new ArrayList() {{
add("Hello"); // быстрая инициализация
add(" world!");
}};
list.trimToSize();
Основные моменты ArrayList:
Использует внутри обычный массив
Доступ к элементам по индексу за константное время O(1)
Доступ к элементам по значению за линейное время O(n)
Хранит любые значения в том числе и
null
Не синхронизирован
Автоматически увеличивается, но не уменьшается
Класс LinkedList
LinkedList использует для хранения двусвязный список.
Помимо интерфейса List реализует интерфейсы Dequeue и Queue.
Соединяет функциональность работы со списком и фукциональность очереди.
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
List<String> list = new LinkedList<>();
list.add(" world!"); // добавить в конец списка
list.add(0, "Hello"); // добавить по индексу
list.size(); // количество элементов в списке
list.remove("Hello"); // удалить по значению
list.remove(1); // удалить по индексу
У каждого элемента LinkedList имеется ссылки на предыдущий и следующий элементы списка. Напоминает связанные звенья одной цепи.
По этим ссылкам можно последовательно переходить от одного элемента к другому.
Для установки таких ссылок LinkedList использует объекты своего вложенного класса Node.
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
У LinkedList есть методы для работы с началом и концом списка, которых нет в ArrayList:
|
Вставляет элемент в начало списка. |
|
Вставляет элемент в конец списка. |
|
Удаляет и возвращает первый элемент из этого списка. |
|
Удаляет и возвращает последний элемент из этого списка. |
Выгода использования LinkedList в работе с серединой списка.
Вставка и удаление в середину LinkedList устроены гораздо проще, чем в ArrayList: для этого внутри списка просто переопределятся ссылки на соседние элементы.
НО! Все элементы массива ArrayList находятся в одном блоке памяти, и операция по сдвигу элементов массива выполняются быстрым низкоуровневым методом System.arraycopy()
Обычно весь внутренний массив попадает в кэш процессора, поэтому элементы массива сдвигаются даже не в памяти, а в кэше.
Все это делает использование LinkedList редким случаем.
Основные моменты LinkedList:
Не синхронизирован
Позволяет хранить любые повторяющиеся объекты, в том числе
null
За константное время O(1) выполняются операции вставки и удаления как первого и последнего элементов, так и элемента из середины списка.
Время поиска позиции элемента осуществляется за линейное время O(n)
Операции поиска элемента по индексу и по значению выполняются за линейное время O(n)
Интерфейс Map
Map – это массив на 16 buckets (корзин) – от 0 до 15.
Расширяется: 16 – 32 – 64 – и далее переходит к структуре red-black tree.
Хранит данные парами: key – value
Ключ всегда является уникальным.
Доступ к значениям осуществляется по ключу.
Ключ нельзя получить по значению – значения могут быть повторяющимися.
public interface Map<K,V>
Map хорошо сбалансирована, когда хорошо предопределены методы hashcode()
у хранимых в ней элементов.
Худший результат работы мапы – Коллизия (когда все элементы попадают в один bucket и их приходится перебирать итерацией).
Изначально, bucket в HashMap представляет из себя связный список LinkedList.
При возникновении Коллизии очередная пара ключ-значение добавляется в этот список.
Если размер связного списка становится равен 8 элементам, происходит преобразование связного списка в дерево.
При этом, найти элемент в HashMap в худшем случае уже можно за O(log(n))
, а не за O(n)
, как в связном списке.
Основные методы Map:
|
Удаляет все пары ключ-значение из этой мапы. |
|
Возвращает |
|
Возвращает |
|
Возвращает Set сопоставлений (пар ключ-значение), содержащихся на этой карте. |
|
Возвращает значение, с которым сопоставлен указанный ключ, или |
|
Возвращает значение, с которым сопоставлен указанный ключ, или |
|
Возвращает |
|
Возвращает Set из ключей, содержащихся в этой маме. |
|
Связывает указанное значение с указанным ключом и кладет это сопоставление в мапу. |
|
Копирует все сопоставления с указанной мапы на эту мапу. |
|
Если указанный ключ отсутствует в мапе (или сопоставлен с нулевым значением), связывает его с заданным значением, кладет пару в мапу и возвращает |
|
Удаляет значение по ключу из этой мапы, если оно присутствует. |
|
Удаляет запись для указанного ключа только в том случае, если она в настоящее время сопоставлена с указанным значением. |
|
Заменяет значение для указанного ключа только в том случае, если он в настоящее время уже сопоставлен с каким-либо значением. |
|
Возвращает количество сопоставлений ключ-значение в этой мапе. |
|
Возвращает Collection значений, содержащихся на этой карте. |
Интерфейс Map.Entry
Вложенный nested интерфейс Map.
Это структура внутри Map, которая хранит все пары ключ-значение.
Возвращает множество Set пар, которое можно перебрать Итератором.
public static interface Map.Entry<K,V>
Объекты Map.Entry действительны только во время итерации.
Map<String, String> map = new HashMap<>();
Set set = map.entrySet(); // множество сопосталений ключ-значение
Iterator iterator = set.iterator(); // итерация по элементам множества
while(iterator.hasNext()) {
Map.Entry entry = (Map.Entry)iterator.next();
System.out.print(entry.getKey() + ": ");
System.out.println(entry.getValue());
}
Класс HashMap
HashMap использует хэш-таблицу для реализации интерфейса Map.
Хранит значения в произвольном порядке.
Структура хранения данных: bucket (корзина).
Позволяет задавать ключ или значение ключевым словом null
Объекты с null
ключами всегда записываются в нулевую ячейку массива.
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
Хеш-таблица – структура данных, реализующая интерфейс ассоциативного массива (сопоставления ключ–значение или entry), которая обеспечивает быструю вставку и поиск элементов.
Хеш-функция получает ключ, а на выходе возвращает целое число – хеш-код.
По полученному хэш-коду ключ помещается по определенному индексу хеш-таблицы.
За хеш-функцию отвечает метод hashCode()
По этой причине важно, чтобы хеш-функция вела себя последовательно и выводила один и тот же хэш-код для одинаковых входных данных.
В качестве ключей рекомендуется использовать только неизменяемые immutable типы, например класс String или Integer.
Хеш-отображение не гарантирует порядок расположения своих элементов.
Порядок, в котором элементы вводятся в хеш-отображение, не обязательно соответствует тому порядку, в котором они извлекаются итератором.
Класс LinkedHashMap
LinkedHashMap поддерживает связанный список записей на Map в том порядке, в котором они были вставлены.
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
Класс TreeMap
Структура хранения данных красно-черное сбалансированное дерево.
По-умолчанию TreeMap сортируется по ключам с использованием принципа натуральной сортировки, но это поведение может быть настроено под конкретную задачу при помощи объекта класса Comparator.
Гарантирует, что все элементы будут отсортированы в порядке возрастания ключа.
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, Serializable
Красно-черное дерево – это самобалансирующаяся версия двоичного поиска.
Обеспечивает логарифмическую сложность О(log(n))
операций добавления, удаления и поиска.
Верхний элемент – это корень дерева (чёрный цвет).
Все остальные элементы распределяются налево или направо в зависимости от значения хешей:
Все левые потомки должны быть меньше корневого узла (или равны ему)
Все правые потомки должны быть больше
Интерфейс Set
Set – это множество однотипных элементов.
Добавляет ограничение, которое запрещает повторяющиеся элементы.
Эквивалентность элементов проверяется с помощью метода equals()
public interface Set<E> extends Collection<E>
У Set нет индексов – нельзя получить элемент или записать значение в коллекцию по определенному индексу.
Можно делать три типа операций: добавлять, удалять элементы и проверять, есть ли во множестве этот элемент. Методов get()
и set()
нет.
Основные методы Set:
|
Добавляет указанный элемент в это множество, если он отсутствует. |
|
Добавляет все элементы из указанной коллекции в это множество, если они еще не присутствуют. |
|
Удаляет все элементы из этого множества. |
|
Возвращает |
|
Возвращает |
|
Удаляет указанный элемент из этого множества, если он присутствует. |
|
Удаляет из этого множества все элементы, содержащиеся в указанной коллекции. |
|
Сохраняет только те элементы в этом множестве, которые содержатся в указанной коллекции. |
|
Возвращает количество элементов в этом множестве. |
Класс HashSet
Класс HashSet расширяет AbstractSet и реализует интерфейс Set.
Не имеет собственной реализации – под капотом использует HashMap.
В качестве значения value HashMap используется заглушка.
Значения HashSet – это ключи внутренней HashMap.
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, Serializable
Основные особенности HashSet:
Операции
add()
,remove()
,contains()
иsize()
за константное время O(1)Порядок элементов может меняться
Класс LinkedHashSet
LinkedHashSet поддерживает связанный список записей во Множестве, в том порядке, в котором они были добавлены.
Это позволяет выполнять итерацию порядка вставки по Множеству.
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, Serializable
Класс TreeSet
TreeSet использует под капотом TreeMap.
Использует дерево для хранения.
Объекты хранятся в отсортированном и возрастающем порядке.
public class TreeSet<E>
extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, Serializable
Основные особенности TreeSet:
Временная сложность базовых операций
add()
,remove()
,contains()
O(log(n))Гарантирует порядок элементов
Класс Collections
Класс Collections содержит различные статические методы для работы с Коллекциями.
Основные методы Collections:
|
Добавляет все указанные элементы в указанную коллекцию. |
|
Поиск указанного объекта в указанном List с помощью алгоритма двоичного поиска. |
|
Копирует все элементы из одного List в другой. |
|
Возвращает |
|
Возвращает пустой List. |
|
Возвращает пустую Map. |
|
Возвращает пустой Set. |
|
Заменяет все элементы указанного списка указанным элементом. |
|
Возвращает количество элементов в указанной коллекции, которые равны указанному объекту. |
|
Возвращает максимальный элемент данной коллекции в соответствии с естественным упорядочением ее элементов. |
|
Возвращает минимальный элемент данной коллекции в соответствии с естественным порядком ее элементов. |
|
Возвращает неизменяемый List, состоящий из n копий указанного объекта. |
|
Возвращает Set из указанной Map. |
|
Заменяет все указанные значения в List другим значением. |
|
Изменяет порядок элементов в указанном List в обратном порядке. |
|
Возвращает Компаратор, который накладывает обратный естественный порядок на коллекцию объектов, реализующих сопоставимый интерфейс. |
|
Изменяет очередь элементов в указанном List на указанном промежутке. |
|
Случайным образом переставляет указанный List, используя источник случайности по умолчанию. |
|
Возвращает неизменяемый Set, содержащий только один указанный объект. |
|
Возвращает неизменяемый List, содержащий только один указанный объект. |
|
Возвращает неизменяемую Map, содержащую только одно сопоставление ключ - значение. |
|
Сортирует указанный List по возрастанию в соответствии с естественным порядком его элементов. |
|
Замена местами двух элементов в указанных позициях в указанном List. |
|
Возвращает синхронизированную коллекцию, поддерживаемую указанной коллекцией. |
|
Возвращает неизменяемую указанную коллекцию. |
Потокобезопасные Concurrent коллекции
Concurrent Collections – это набор коллекций, более эффективно работающих в многопоточной среде чем стандартные коллекции из пакета java.util
Вместо базового враппера (обертки) Collections.synchronizedCollection
с блокированием доступа ко всей коллекции используются блокировки по сегментам данных или же оптимизируется работа для параллельного чтения данных по wait-free алгоритмам.
Классы Hashable и Collections.synchronizedMap
синхронизированы.
Пакет java.util.concurrent
предоставляет реализации одновременных коллекций:
ConcurrentHashMap – вместо HashMap
ConcurrentSkipListMap – вместо TreeMap
ConcurrentSkipListSet
CopyOnWriteArrayList – вместо ArrayList
CopyOnWriteArraySet
ConcurrentHashMap безопасно допускает любое количество одновременных операций чтения, а также большое количество одновременных записей.
Синхронизированные классы могут быть полезны, когда нужно предотвратить весь доступ к коллекции с помощью одной блокировки за счет более низкой масштабируемости. В других случаях, когда ожидается доступ к общей коллекции нескольких потоков, одновременные версии, как правило, предпочтительнее.
Сортировка коллекций
Чтобы сортировать объекты в коллекции – нужно прописать правила их сравнения.
Для этого в классе, чьи объекты будут сортированы, должен быть интерфейс Comparable или Comparator.
Comparable – естественная сортировка класса
Comparator – используется, когда в классе не реализован Comparable, либо реализован, но с неподходящей нам логикой сравнения объектов.
Три важных свойства сравнивающей функции
Если сравнивающая функция не удовлетворяет этим свойствам, алгоритм может выдать совершенно непредсказуемый результат.
Рефлексивность – сравнение элемента с самим собой всегда возвращает
0
Антисимметричность – сравнение
A
сB
, иB
сA
должны дать разный знак.Транзитивность – если сравнение
A
сB
, иB
сC
выдает одинаковый знак, то и сравнениеA
сC
должно выдать такой же знак.
Интерфейс Comparable
Интерфейс Comparable накладывает полный порядок на объекты каждого класса, который его реализует.
Этот порядок называется естественный порядок класса, а метод compareTo()
класса называется его естественным методом сравнения.
public interface Comparable<T>
Списки и массивы объектов, реализующих этот интерфейс, могут быть автоматически отсортированы методами Collections.sort()
и Arrays.sort()
.
Объекты, реализующие этот интерфейс, могут использоваться в качестве ключей в SortedMap или как элементы в SortedSet без необходимости указания Компаратора.
Чтобы задать свой способ сравнения объектов, нужно переопределить метод compareTo()
, где прописать логику сравнения.
class Person implements Comparable<Person> {
private String fullName;
private Integer uuid;
@Override
public int compareTo(Person person) {
return uuid - person.uuid;
}
}
Метод compareTo(Object o)
сравнивает этот объект с указанным объектом. Возвращает отрицательное целое число, ноль или положительное целое число, если этот объект меньше, равен или больше указанного объекта.
В предопределенном методе вместо выражения можно использовать его:
@Override
public int compareTo(Person person) {
return uuid.compareTo(person.uuid);
}
Рекомендуется чтобы (x.compareTo(y)==0)
был равен (x.equals(y))
Можно сравнивать по нескольким полям класса.
Например, если fullName
совпадают – тогда сравнить по uuid
@Override
public int compareTo(Person person) {
int fullNameComparator = fullName.compareTo(person.fullName);
int uuidComparator = uuid.compareTo(person.uuid);
return (fullNameComparator != 0 ? fullNameComparator : uuidComparator);
}
Интерфейс Comparator
Comparator используется, когда в классе не реализован, либо реализован с неподходящей логикой интерфейс Comparable, от чего нельзя сравнить объекты нужным образом.
@FunctionalInterface
public interface Comparator<T>
Можно создать отдельный класс компаратор, реализовав в нем Comparator.
Такой класс будет содержать нужную логику сравнения и его можно будет использовать в аргументах методов, которые требуют Компаратор.
public class PersonUuidComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.uuid - p2.uuid;
}
}
Можно создать несколько Компараторов для класса и использовать нужный.
public class PersonNameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.uuid);
}
}
Можно применять сразу несколько компараторов по принципу приоритета thenComparing()
Comparator<Person> personComparator = new PersonNameComparator()
.thenComparing(new PersonUuidComparator());
Comparator – функциональный интерфейс, его можно задать лямбда выражением.
Статический метод Comparator.comparing
принимает функцию ключа сортировки и возвращает компаратор для типа, содержащего ключ сортировки.
Comparator<Person> personUuidComparator = Comparator.comparing(Person::getUuid);
List<Person> people = new ArrayList<Person>();
people.sort((person1, person2) -> person1.uuid - person2.uuid());
people.sort((person1, person2) -> person1.uuid.compareTo(person2.uuid()));
Для сортировки в обратном порядке reversed()
Comparator<Person> uuidComparator =
(person1, person2) -> person1.getUuid().compareTo(person2.getUuid());
people.sort(uuidComparator.reversed());