Java for Beginner
676 subscribers
545 photos
155 videos
12 files
836 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Что выведет код?

public class ArithmeticChallenge {
public static void main(String[] args) {
int result = mysteriousArithmetic(3, 2);
System.out.println(result);
}

public static int mysteriousArithmetic(int a, int b) {
int result = 0;
for (int i = 1; i <= a; i++) {
result += (i % 2 == 0) ? (i * b) : (i + b);
}
return result;
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
0%
1
90%
12
10%
18
0%
36
0%
-3
Основные методы HashMap и их устройство

HashMap предоставляет множество методов для работы с парами "ключ-значение". Рассмотрим основные методы и разберем их внутреннее устройство.

1. put(K key, V value)
Метод put добавляет или обновляет значение для указанного ключа.

Устройство метода:
Вычисление хэш-кода ключа: Метод вызывает key.hashCode().
Вычисление индекса бакета: Используется побитовая операция AND с (n-1), где n - размер массива бакетов.
Поиск существующего ключа в бакете: Если ключ уже существует, значение обновляется.
Вставка новой пары в бакет: Если ключ не существует, создается новый узел и добавляется в бакет.


public V put(K key, V value) {
// Вычисление хэш-кода
int hash = hash(key);
// Вычисление индекса бакета
int bucketIndex = indexFor(hash, table.length);
// Поиск существующего ключа и обновление значения
for (Entry<K, V> e = table[bucketIndex]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
// Вставка новой пары ключ-значение
addEntry(hash, key, value, bucketIndex);
return null;
}


Пример использования:
import java.util.HashMap;

public class Main {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("One", 1);
map.put("Two", 2);
System.out.println(map); // Output: {One=1, Two=2}
}
}


2. get(Object key)
Метод get возвращает значение, связанное с указанным ключом.

Устройство метода:
Вычисление хэш-кода ключа: Метод вызывает key.hashCode().
Вычисление индекса бакета: Используется побитовая операция AND.
Поиск в бакете: Последовательно проверяются элементы в бакете.

public V get(Object key) {
// Вычисление хэш-кода
int hash = hash(key);
// Поиск в бакете
for (Entry<K, V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
return e.value;
}
}
return null;
}


Пример использования:
import java.util.HashMap;

public class Main {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("One", 1);
System.out.println(map.get("One")); // Output: 1
}
}


3. remove(Object key)
Метод remove удаляет пару "ключ-значение" по указанному ключу.

Устройство метода:
Вычисление хэш-кода ключа: Метод вызывает key.hashCode().
Вычисление индекса бакета: Используется побитовая операция AND.
Поиск и удаление: Ищется элемент в бакете и удаляется, если найден.

public V remove(Object key) {
Entry<K, V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}

private Entry<K, V> removeEntryForKey(Object key) {
// Вычисление хэш-кода
int hash = hash(key);
int bucketIndex = indexFor(hash, table.length);
Entry<K, V> prev = table[bucketIndex];
Entry<K, V> e = prev;

while (e != null) {
Entry<K, V> next = e.next;
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
if (prev == e) {
table[bucketIndex] = next;
} else {
prev.next = next;
}
return e;
}
prev = e;
e = next;
}
return null;
}


Пример использования:
import java.util.HashMap;

public class Main {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("One", 1);
map.put("Two", 2);
map.remove("One");
System.out.println(map); // Output: {Two=2}
}
}

#Java #Training #Medium #HashMap
4. containsKey(Object key)
Метод containsKey проверяет, существует ли указанная ключ в HashMap.

Устройство метода:
Использует метод get: Если метод get возвращает не null, значит ключ существует.
public boolean containsKey(Object key) {
return getEntry(key) != null;
}

final Entry<K, V> getEntry(Object key) {
// Вычисление хэш-кода
int hash = (key == null) ? 0 : hash(key);
// Поиск в бакете
for (Entry<K, V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
return e;
}
}
return null;
}


Пример использования:
import java.util.HashMap;

public class Main {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("One", 1);
System.out.println(map.containsKey("One")); // Output: true
System.out.println(map.containsKey("Two")); // Output: false
}
}


5. containsValue(Object value)
Метод containsValue проверяет, существует ли указанное значение в HashMap.

Устройство метода:
Проходит по всем бакетам: Проверяет каждый элемент на наличие значения.

public boolean containsValue(Object value) {
if (value == null) {
for (Entry<K, V> e : table)
for (Entry<K, V> ee = e; ee != null; ee = ee.next)
if (ee.value == null)
return true;
} else {
for (Entry<K, V> e : table)
for (Entry<K, V> ee = e; ee != null; ee = ee.next)
if (value.equals(ee.value))
return true;
}
return false;
}


Пример использования:
import java.util.HashMap;

public class Main {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("One", 1);
System.out.println(map.containsValue(1)); // Output: true
System.out.println(map.containsValue(2)); // Output: false
}
}


6. size()
Метод size возвращает количество пар "ключ-значение" в HashMap.

Устройство метода:
Хранение размера: HashMap хранит размер как переменную size.
public int size() {
return size;
}
Пример использования:
java
Копировать код
import java.util.HashMap;

public class Main {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("One", 1);
map.put("Two", 2);
System.out.println(map.size()); // Output: 2
}
}


7. clear()
Метод clear удаляет все пары "ключ-значение" из HashMap.

Устройство метода:
Устанавливает массив бакетов в null: И обнуляет размер.
public void clear() {
Entry<K, V>[] tab = table;
for (int i = 0; i < tab.length; i++)
tab[i] = null;
size = 0;
}


Пример использования:
import java.util.HashMap;

public class Main {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("One", 1);
map.put("Two", 2);
map.clear();
System.out.println(map.size()); // Output: 0
}
}


#Java #Training #Medium #HashMap
ConcurrentHashMap, отличие от HashMap

ConcurrentHashMap — это класс в пакете java.util.concurrent, который представляет собой потокобезопасную версию HashMap. Он разработан для использования в многопоточных приложениях, где необходимо гарантировать корректность и производительность при одновременном доступе к коллекции из нескольких потоков.


Основные характеристики ConcurrentHashMap

Потокобезопасность: ConcurrentHashMap реализует эффективные механизмы блокировок для обеспечения потокобезопасного доступа к данным. В отличие от Hashtable, который синхронизирует все методы, ConcurrentHashMap использует более изящные механизмы управления доступом.

Производительность: За счет использования сегментации (в старых версиях) или уменьшенных блокировок (в новых версиях), ConcurrentHashMap обеспечивает высокую производительность в условиях многопоточного доступа.


Структура ConcurrentHashMap

Сегментация (в старых версиях): В старых версиях ConcurrentHashMap использовалась сегментация, при которой таблица хешей разбивалась на сегменты, каждый из которых был самостоятельной хеш-таблицей с собственной блокировкой. Это позволяло одновременно выполнять операции в разных сегментах без конфликтов.

Lock Striping (в новых версиях): В новых версиях ConcurrentHashMap используется механизм Lock Striping, который включает в себя более мелкие блокировки на уровне бакетов или групп бакетов, что снижает вероятность блокировок и увеличивает производительность.


Отличия ConcurrentHashMap от HashMap

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

Блокировки: HashMap не использует блокировки, что делает его небезопасным для многопоточного использования. ConcurrentHashMap, напротив, использует сегментацию или мелкие блокировки, что позволяет безопасно выполнять параллельные операции.

Производительность: В условиях многопоточного доступа HashMap может демонстрировать низкую производительность и некорректное поведение. ConcurrentHashMap оптимизирован для многопоточного доступа и обеспечивает более высокую производительность за счет эффективного управления блокировками.

Методы атомарных операций: ConcurrentHashMap предоставляет методы атомарных операций, такие как putIfAbsent, remove с проверкой значения и replace, которые отсутствуют в HashMap.


Пример использования ConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

public class ConcurrentHashMapExample {
public static void main(String[] args) {
// Создание ConcurrentHashMap
Map<String, Integer> map = new ConcurrentHashMap<>();
// Добавление элементов
map.put("Apple", 50);
map.put("Banana", 30);
map.put("Orange", 20);
// Получение значения по ключу
System.out.println("Price of Apple: " + map.get("Apple"));
// Проверка наличия ключа
if (map.containsKey("Banana")) {
System.out.println("Banana is in the list.");
}
// Удаление элемента
map.remove("Orange");
// Перебор элементов
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Использование атомарных операций
map.putIfAbsent("Grapes", 40);
map.replace("Banana", 35);
// Перебор элементов после атомарных операций
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}


Полезные ссылки для более полного ознакомления с ConcurrentHashMap (спасибо авторам за их кропотливую работу):
https://habr.com/ru/articles/132884/
https://www.baeldung.com/java-concurrent-map


#Java #Training #Medium #ConcurrentHashMap
Как вы относитесь к тому, что посты иногда разорваны? (Это из-за большого объема информации)
Anonymous Poll
67%
Все нормально
17%
Очень неудобно читать
17%
Без разницы
Что выведет код?

import java.util.HashMap;
import java.util.Map;

public class HashMapChallenge {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
map.put("four", 4);

int result = processMap(map);
System.out.println(result);
}

public static int processMap(Map<String, Integer> map) {
int sum = 0;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getKey().length() % 2 == 0) {
sum += entry.getValue() * 2;
} else {
sum += entry.getValue() * 3;
}
}
return sum;
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
20%
18
60%
26
20%
-8
0%
139
Внутренняя структура ConcurrentHashMap

ConcurrentHashMap состоит из хеш-таблицы, которая делится на сегменты (в старых версиях) или использует мелкие блокировки (в новых версиях), чтобы минимизировать блокировки и увеличить параллелизм.

Бакеты (Buckets):

Подобно HashMap, ConcurrentHashMap использует массив бакетов для хранения пар "ключ-значение".
Внутри каждого бакета используется связанный список (или дерево в случае большого числа элементов) для хранения элементов, что позволяет управлять коллизиями.


Сегменты (Segments) (в старых версиях):


В старых версиях ConcurrentHashMap массив бакетов разбивался на сегменты, каждый из которых имел свою собственную блокировку. Это позволяло нескольким потокам одновременно модифицировать разные сегменты без взаимных блокировок.
Каждый сегмент был отдельной хеш-таблицей с собственными методами вставки, поиска и удаления.


Lock Striping (в новых версиях):

В новых версиях (Java 8 и позже) ConcurrentHashMap использует более мелкие блокировки, известные как lock striping.
Вместо сегментов используется механизм блокировок на уровне бакетов или групп бакетов, что уменьшает вероятность блокировок и увеличивает производительность.


Механизмы синхронизации

Мелкие блокировки (Fine-Grained Locks):

ConcurrentHashMap использует мелкие блокировки для управления доступом к отдельным бакетам или небольшим группам бакетов.
Это позволяет нескольким потокам одновременно выполнять операции над различными частями хеш-таблицы, минимизируя блокировки и повышая параллелизм.


CAS (Compare-And-Swap):

Для некоторых операций, таких как вставка и обновление значений, используется механизм CAS, который позволяет атомарно обновлять значения без использования явных блокировок.
CAS операции обеспечивают высокую производительность и минимизируют затраты на синхронизацию.


Механизм Reentrant Lock:

Внутренне для блокировок ConcurrentHashMap использует ReentrantLock, что позволяет потокам безопасно входить в блокировку несколько раз.
ReentrantLock предоставляет больше возможностей управления, таких как блокировка с тайм-аутом и попытка блокировки без ожидания.


Внутреннее устройство бакетов

Каждый бакет в ConcurrentHashMap представлен объектом типа Node:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V value;
volatile Node<K,V> next;

Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}

public final K getKey() {
return key;
}

public final V getValue() {
return value;
}

public final String toString() {
return key + "=" + value;
}

public final int hashCode() {
return key.hashCode() ^ value.hashCode();
}

public final V setValue(V value) {
throw new UnsupportedOperationException();
}

public final boolean equals(Object o) {
Object k, v, u;
Map.Entry<?,?> e;
return ((o instanceof Map.Entry) &&
(k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
(v = e.getValue()) != null &&
(k == key || k.equals(key)) &&
(v == (u = value) || v.equals(u)));
}
}


#Java #Training #Medium #ConcurrentHashMap
Механизмы синхронизации

Пример использования CAS для вставки элемента:
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.value;
if (!onlyIfAbsent)
e.value = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.value;
if (!onlyIfAbsent)
p.value = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}


#Java #Training #Medium #ConcurrentHashMap
LinkedHashMap

LinkedHashMap
— это класс из пакета java.util, который является расширением HashMap и сохраняет порядок вставки или порядок доступа к элементам. Это достигается за счет использования связанного списка, который поддерживает порядок элементов.

Основные характеристики LinkedHashMap

Порядок элементов: LinkedHashMap поддерживает порядок элементов, либо порядок вставки, либо порядок последнего доступа, в зависимости от конфигурации.
Производительность: Поиск, вставка и удаление элементов имеют сложность O(1), аналогично HashMap.
Связанный список: Внутренне LinkedHashMap использует связанный список для поддержания порядка элементов.

Структура LinkedHashMap

Бакеты (Buckets):

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

Связанный список:

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

Два порядка:

Порядок вставки (Insertion Order): Элементы хранятся в порядке их добавления в карту.
Порядок доступа (Access Order): Элементы перемещаются в конец списка при их доступе, что полезно для реализации LRU-кэшей (Least Recently Used).


Создание и использование LinkedHashMap
import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapExample {
public static void main(String[] args) {
// Создание LinkedHashMap с порядком вставки
LinkedHashMap<String, Integer> insertionOrderMap = new LinkedHashMap<>();

// Добавление элементов
insertionOrderMap.put("Apple", 50);
insertionOrderMap.put("Banana", 30);
insertionOrderMap.put("Orange", 20);

// Перебор элементов в порядке вставки
System.out.println("Insertion Order:");
for (Map.Entry<String, Integer> entry : insertionOrderMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}

// Создание LinkedHashMap с порядком доступа
LinkedHashMap<String, Integer> accessOrderMap = new LinkedHashMap<>(16, 0.75f, true);

// Добавление элементов
accessOrderMap.put("Apple", 50);
accessOrderMap.put("Banana", 30);
accessOrderMap.put("Orange", 20);

// Доступ к элементу, что изменит его порядок
accessOrderMap.get("Banana");

// Перебор элементов в порядке доступа
System.out.println("\nAccess Order:");
for (Map.Entry<String, Integer> entry : accessOrderMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}


Внутреннее устройство LinkedHashMap

LinkedHashMap расширяет HashMap и добавляет дополнительную функциональность для поддержания порядка элементов.

Класс Entry:

Внутренний класс Entry наследуется от HashMap.Entry и добавляет ссылки на предыдущий и следующий элементы, что образует двусвязный список.

static class Entry<K, V> extends HashMap.Node<K, V> {
Entry<K, V> before, after;

Entry(int hash, K key, V value, HashMap.Node<K, V> next) {
super(hash, key, value, next);
}
}


Указатели на первый и последний элементы:

LinkedHashMap содержит указатели на первый и последний элементы списка, что позволяет быстро выполнять вставку и удаление элементов в конец списка.
transient LinkedHashMap.Entry<K, V> head;
transient LinkedHashMap.Entry<K, V> tail;

#Java #Training #Medium #LinkedHashMap
Методы для управления порядком:

LinkedHashMap переопределяет методы newNode и afterNodeAccess, чтобы управлять порядком элементов.

Node<K, V> newNode(int hash, K key, V value, Node<K, V> e) {
LinkedHashMap.Entry<K, V> p = new LinkedHashMap.Entry<>(hash, key, value, e);
linkNodeLast(p);
return p;
}

void afterNodeAccess(Node<K, V> e) {
LinkedHashMap.Entry<K, V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K, V> p = (LinkedHashMap.Entry<K, V>) e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}


Порядок вставки (Insertion Order)
import java.util.LinkedHashMap;
import java.util.Map;

public class InsertionOrderExample {
public static void main(String[] args) {
// Создание LinkedHashMap с порядком вставки
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();

// Добавление элементов
map.put("Apple", 50);
map.put("Banana", 30);
map.put("Orange", 20);
map.put("Grapes", 40);

// Перебор элементов в порядке вставки
System.out.println("Insertion Order:");
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}


В этом примере элементы добавляются в порядке "Apple", "Banana", "Orange", "Grapes", и при переборе они выводятся в том же порядке.

Порядок доступа (Access Order)
import java.util.LinkedHashMap;
import java.util.Map;

public class AccessOrderExample {
public static void main(String[] args) {
// Создание LinkedHashMap с порядком доступа
LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, true);

// Добавление элементов
map.put("Apple", 50);
map.put("Banana", 30);
map.put("Orange", 20);
map.put("Grapes", 40);

// Доступ к элементу, что изменит его порядок
map.get("Banana");
map.get("Apple");

// Перебор элементов в порядке доступа
System.out.println("Access Order:");
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}


В этом примере элементы добавляются в порядке "Apple", "Banana", "Orange", "Grapes". После доступа к "Banana" и "Apple" они перемещаются в конец списка, и при переборе порядок будет "Orange", "Grapes", "Banana", "Apple".

Ссылки на полезные статьи (спасибо авторам за проделанную работу) :
https://habr.com/ru/articles/129037/
https://www.baeldung.com/java-linked-hashmap

#Java #Training #Medium #LinkedHashMap
Что выведет код?

import java.util.ArrayList;
import java.util.List;

public class ListConcatenationChallenge {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
list1.add("c");

List<String> list2 = new ArrayList<>();
list2.add("d");
list2.add("e");
list2.add("f");

String result = concatenateLists(list1, list2);
System.out.println(result);
}

public static String concatenateLists(List<String> list1, List<String> list2) {
StringBuilder sb = new StringBuilder();

for (int i = 0; i < list1.size(); i++) {
sb.append(list1.get(i));
sb.append(list2.get(i));
}

return sb.toString();
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
0%
"abcdef"
25%
"adbcef"
75%
"adbecf"
0%
"abcdfe"
Ну хоть что-то ищет😂

https://t.me/Java_for_beginner_dev

#Mems
Внутреннее устройство LinkedHashMap

1. Связанный список для порядка элементов

LinkedHashMap расширяет HashMap, добавляя поддержку связанного списка для поддержания порядка элементов. Каждый элемент хранится в виде узла двусвязного списка, что позволяет поддерживать порядок вставки или порядок доступа.
static class Entry<K, V> extends HashMap.Node<K, V> {
Entry<K, V> before, after;

Entry(int hash, K key, V value, HashMap.Node<K, V> next) {
super(hash, key, value, next);
}
}


2. Поля для управления порядком

LinkedHashMap добавляет два новых поля для управления порядком элементов:

transient LinkedHashMap.Entry<K, V> head;
transient LinkedHashMap.Entry<K, V> tail;

Эти поля указывают на первый и последний элементы в связанном списке.

3. Механизм управления порядком элементов

Для управления порядком элементов LinkedHashMap переопределяет методы newNode, afterNodeAccess и afterNodeInsertion:

newNode: Создает новый узел и добавляет его в конец связанного списка.
Node<K, V> newNode(int hash, K key, V value, Node<K, V> e) {
LinkedHashMap.Entry<K, V> p = new LinkedHashMap.Entry<>(hash, key, value, e);
linkNodeLast(p);
return p;
}

private void linkNodeLast(LinkedHashMap.Entry<K, V> p) {
LinkedHashMap.Entry<K, V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}


afterNodeAccess: Обновляет порядок элементов при доступе, если карта настроена на порядок доступа.
void afterNodeAccess(Node<K, V> e) {
LinkedHashMap.Entry<K, V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K, V> p = (LinkedHashMap.Entry<K, V>) e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}


afterNodeInsertion: Удаляет самый старый элемент, если карта настроена на удаление старейших элементов.
void afterNodeInsertion(boolean evict) {
LinkedHashMap.Entry<K, V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}


Основные методы LinkedHashMap


1. Конструкторы

LinkedHashMap предоставляет несколько конструкторов для создания карты с различными настройками:

public LinkedHashMap() {
super();
}

public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
}

public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}

public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}


2. Метод put
Метод put добавляет пару ключ-значение в карту. Если ключ уже существует, его значение обновляется.
public V put(K key, V value) {
return super.put(key, value);
}


3. Метод get

Метод get возвращает значение, связанное с указанным ключом. Если ключ не найден, возвращается null. При этом порядок элементов обновляется, если карта настроена на порядок доступа.

public V get(Object key) {
Node<K, V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}


4. Метод remove
Метод remove удаляет пару ключ-значение по указанному ключу и возвращает удаленное значение.
public V remove(Object key) {
Node<K, V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;
}


5. Метод containsKey


Метод containsKey проверяет, содержит ли карта указанный ключ.
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}


#Java #Training #Medium #LinkedHashMap
6. Метод containsValue

Метод containsValue проверяет, содержит ли карта указанное значение.

public boolean containsValue(Object value) {
for (Node<K, V> e = head; e != null; e = e.after) {
if (value.equals(e.value))
return true;
}
return false;
}


7. Метод clear


Метод clear очищает карту, удаляя все элементы.
public void clear() {
super.clear();
head = tail = null;
}


8. Метод entrySet

Метод entrySet возвращает набор всех пар ключ-значение в карте.
public Set<Map.Entry<K, V>> entrySet() {
return new LinkedEntrySet();
}


#Java #Training #Medium #LinkedHashMap
"Если мы в ответе за тех кого приручили, в ответе ли мы за то, что они спроектировали?"🧐

#Mems
Введение в TreeMap, принципы работы и внутреннее устройство

TreeMap — это реализация интерфейса NavigableMap, основанная на красно-черном дереве. Он предоставляет отсортированное отображение ключей и является мощным инструментом для хранения отсортированных данных.

Основные особенности TreeMap

Отсортированное отображение:
Ключи в TreeMap всегда отсортированы в порядке возрастания, если не указан другой компаратор.
Красно-черное дерево: Использует самобалансирующееся бинарное дерево для хранения элементов.
Эффективность: Операции вставки, удаления и поиска выполняются за O(log n).


Внутреннее устройство TreeMap


Красно-черное дерево — это тип самобалансирующегося бинарного дерева поиска с следующими свойствами:

Каждый узел либо красный, либо черный.
Корень дерева всегда черный.
Все листья (NULL-указатели) считаются черными.
Красный узел не может иметь красных дочерних узлов (т.е., красные узлы не могут быть рядом).
Любой путь от корня до листа содержит одинаковое количество черных узлов.


Класс Entry

Внутренний класс Entry представляет узел в дереве:

static final class Entry<K, V> implements Map.Entry<K, V> {
K key;
V value;
Entry<K, V> left, right, parent;
boolean color = BLACK;

Entry(K key, V value, Entry<K, V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}

public K getKey() { return key; }
public V getValue() { return value; }
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
}


Принципы работы TreeMap

Вставка
При вставке элемента TreeMap сначала находит подходящее место для нового элемента, а затем вставляет его как красный узел и балансирует дерево.

Удаление
Удаление элемента включает три основных шага:
Найти узел для удаления.
Удалить узел.
Балансировать дерево, если это необходимо.


Поиск
Поиск элемента осуществляется путем последовательного сравнения ключей и движения по дереву (влево или вправо), пока не будет найден нужный ключ.


Основные методы TreeMap


Метод put
Метод put добавляет пару ключ-значение в TreeMap:
public V put(K key, V value) {
Entry<K, V> t = root;
if (t == null) {
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K, V> parent;
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0) t = t.left;
else if (cmp > 0) t = t.right;
else return t.setValue(value);
} while (t != null);
} else {
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0) t = t.left;
else if (cmp > 0) t = t.right;
else return t.setValue(value);
} while (t != null);
}
Entry<K, V> e = new Entry<>(key, value, parent);
if (cmp < 0) parent.left = e;
else parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}


Метод get

Метод get возвращает значение по ключу:
public V get(Object key) {
Entry<K, V> p = getEntry(key);
return (p == null ? null : p.value);
}

final Entry<K, V> getEntry(Object key) {
if (comparator != null) return getEntryUsingComparator(key);
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K, V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0) p = p.left;
else if (cmp > 0) p = p.right;
else return p;
}
return null;
}


Ссылки на полезные статьи (спасибо авторам за проделанную работу) :
https://javarush.com/groups/posts/2584-osobennosti-treemap
https://www.baeldung.com/java-treemap

#Java #Training #Medium #TreeMap