☕ Java

Arrays (Listen) in Java 17: Ausführliches Handbuch

Коллекции Java: ArrayList, HashMap и HashSet для начинающих

Массивы хороши, но их размер фиксирован при создании. В реальных программах данных бывает разное количество: список заказов, результаты поиска, контакты в телефоне. Для этого в Java есть коллекции — гибкие структуры данных с автоматическим изменением размера.

Иерархия коллекций Java (JCF)

java.util — пакет с коллекциями
├── List (упорядоченные, разрешены дубликаты)
│   ├── ArrayList  — быстрый доступ по индексу
│   └── LinkedList — быстрые вставки/удаления
│
├── Set (без дубликатов)
│   ├── HashSet    — быстрый, без порядка
│   └── TreeSet    — отсортированный
│
└── Map (пары ключ=значение)
    ├── HashMap    — быстрый, без порядка
    └── TreeMap    — отсортированный по ключу

ArrayList vs Массив

import java.util.*;

// МАССИВ — фиксированный размер
String[] arr = new String[3];
arr[0]="яблоко"; arr[1]="груша"; arr[2]="банан";
// arr[3]="манго"; // ArrayIndexOutOfBoundsException!

// ARRAYLIST — размер растёт автоматически
ArrayList list = new ArrayList<>();
list.add("яблоко");
list.add("груша");
list.add("банан");
list.add("манго");     // Легко! Размер автоматически увеличится
list.add("апельсин");

System.out.println(list);          // [яблоко, груша, банан, манго, апельсин]
System.out.println(list.size());   // 5 (не length!)
System.out.println(list.get(0));   // яблоко
System.out.println(list.get(list.size()-1)); // апельсин (последний)

ArrayList: полный арсенал операций

import java.util.*;

public class ArrayListOps {
    public static void main(String[] args) {
        ArrayList nums = new ArrayList<>();
        
        // Добавление
        for (int n : new int[]{5,2,8,1,9,3,7}) nums.add(n);
        System.out.println("Исходный: " + nums);
        
        // Вставка по позиции (сдвигает остальные)
        nums.add(2, 100);  // вставить 100 на позицию 2
        System.out.println("После add(2,100): " + nums);
        
        // Замена
        nums.set(0, 50);
        System.out.println("После set(0,50): " + nums);
        
        // Удаление по значению
        nums.remove(Integer.valueOf(100));  // Удалить значение 100
        // Удаление по индексу
        nums.remove(0);                     // Удалить элемент с индексом 0
        System.out.println("После удалений: " + nums);
        
        // Поиск
        System.out.println("Есть 8? " + nums.contains(8));
        System.out.println("Индекс 9: " + nums.indexOf(9));
        
        // Сортировка
        Collections.sort(nums);
        System.out.println("Отсортированный: " + nums);
        
        Collections.sort(nums, Collections.reverseOrder());
        System.out.println("Обратный: " + nums);
        
        // Статистика
        System.out.println("Max: " + Collections.max(nums));
        System.out.println("Min: " + Collections.min(nums));
        
        // Перемешать
        Collections.shuffle(nums);
        System.out.println("Перемешанный: " + nums);
        
        // Очистить
        nums.clear();
        System.out.println("Пустой? " + nums.isEmpty()); // true
        
        // Создать из массива
        List fruits = new ArrayList<>(Arrays.asList("яблоко","груша","банан"));
        System.out.println(fruits);
    }
}

LinkedList: когда вставки важнее доступа

import java.util.LinkedList;

LinkedList list = new LinkedList<>();

// Добавление в начало/конец
list.addFirst("Иван");   // Начало
list.addLast("Мария");   // Конец
list.addFirst("Боб");    // Начало → [Боб, Иван, Мария]

System.out.println(list.getFirst()); // Боб
System.out.println(list.getLast());  // Мария

// Удаление
String f = list.removeFirst(); // "Боб" (очередь FIFO)
String l = list.removeLast();  // "Мария"
System.out.println(list);      // [Иван]

// LinkedList как стек (LIFO: последним вошёл — первым вышел)
LinkedList stack = new LinkedList<>();
stack.push("A"); stack.push("B"); stack.push("C");
System.out.println(stack.pop()); // C (последний вошёл — первый вышел)
System.out.println(stack.pop()); // B

// Когда использовать?
// ArrayList: быстрый доступ по индексу get(i) — O(1)
// LinkedList: быстрые вставки в начало/конец — O(1), но медленный get — O(n)

HashMap: телефонная книга

Аналогия: Телефонная книга. Ищете по имени (ключ) — получаете номер (значение). Мгновенно!

import java.util.*;

public class HashMapDemo {
    public static void main(String[] args) {
        HashMap capitals = new HashMap<>();
        
        // put(ключ, значение)
        capitals.put("Россия",  "Москва");
        capitals.put("Франция", "Париж");
        capitals.put("Германия","Берлин");
        capitals.put("Япония",  "Токио");
        
        // get(ключ) → значение
        System.out.println("Столица Франции: " + capitals.get("Франция")); // Париж
        System.out.println("Столица Италии: "  + capitals.get("Италия"));  // null (нет ключа)
        
        // getOrDefault — значение по умолчанию если ключ не найден
        System.out.println(capitals.getOrDefault("Италия", "Неизвестно")); // Неизвестно
        
        // Проверка наличия
        System.out.println("Есть Япония? " + capitals.containsKey("Япония")); // true
        System.out.println("Есть Токио?  " + capitals.containsValue("Токио")); // true
        
        // Обновление (put перезаписывает)
        capitals.put("Германия", "Берлин (Berlin)");
        
        // Удаление
        capitals.remove("Франция");
        System.out.println("После удаления: " + capitals.size()); // 3
        
        // Перебор всех пар
        System.out.println("Все столицы:");
        for (Map.Entry e : capitals.entrySet()) {
            System.out.println("  " + e.getKey() + " → " + e.getValue());
        }
        
        // Перебор ключей
        for (String country : capitals.keySet())
            System.out.println(country);
        
        // Перебор значений
        for (String capital : capitals.values())
            System.out.println(capital);
        
        // Практика: подсчёт частоты слов
        String text = "java python java java python c++ java";
        HashMap freq = new HashMap<>();
        for (String word : text.split(" ")) {
            freq.put(word, freq.getOrDefault(word, 0) + 1);
        }
        System.out.println("Частота: " + freq); // {java=4, python=2, c++=1}
        
        // Сортировка по значению
        freq.entrySet().stream()
            .sorted(Map.Entry.comparingByValue().reversed())
            .forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
    }
}

HashSet: только уникальные элементы

Аналогия: Ящик для уникальных предметов. Положите два одинаковых — останется один. Удобно для хранения уникальных значений и проверки принадлежности.

import java.util.*;

HashSet cities = new HashSet<>();
cities.add("Москва"); cities.add("Питер");
cities.add("Новосибирск"); cities.add("Москва"); // Дубликат — игнорируется!

System.out.println("Городов: " + cities.size()); // 3 (не 4!)
System.out.println(cities); // [Питер, Новосибирск, Москва] (порядок произвольный)

// contains — O(1)! Мгновенная проверка
System.out.println("Москва есть? " + cities.contains("Москва"));  // true
System.out.println("Лондон есть? " + cities.contains("Лондон"));  // false

// Операции над множествами
HashSet a = new HashSet<>(Arrays.asList(1,2,3,4,5));
HashSet b = new HashSet<>(Arrays.asList(3,4,5,6,7));

// Пересечение
HashSet inter = new HashSet<>(a);
inter.retainAll(b);
System.out.println("Пересечение: " + inter); // [3,4,5]

// Объединение
HashSet union = new HashSet<>(a);
union.addAll(b);
System.out.println("Объединение: " + union); // [1,2,3,4,5,6,7]

// Разность
HashSet diff = new HashSet<>(a);
diff.removeAll(b);
System.out.println("Разность a-b: " + diff); // [1,2]

// Практика: найти уникальные слова
String text = "java python java go python java";
HashSet unique = new HashSet<>(Arrays.asList(text.split(" ")));
System.out.println("Уникальных языков: " + unique.size()); // 3
System.out.println(unique); // [go, java, python]

Iterator: безопасный обход с удалением

import java.util.*;

ArrayList nums = new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7,8,9,10));

// Нельзя удалять элементы в for-each — ConcurrentModificationException!
// for (int n : nums) { if(n%2==0) nums.remove(Integer.valueOf(n)); } // ОШИБКА!

// Правильно: используйте Iterator
Iterator it = nums.iterator();
while (it.hasNext()) {
    int n = it.next();
    if (n % 2 == 0) it.remove(); // Безопасное удаление
}
System.out.println("Нечётные: " + nums); // [1,3,5,7,9]

// Элегантнее (Java 8+): removeIf
ArrayList names = new ArrayList<>(Arrays.asList("Иван","Ал","Мария","Б","Алексей"));
names.removeIf(name -> name.length() < 4); // Удалить короткие имена
System.out.println(names); // [Иван, Мария, Алексей]

Когда какую коллекцию использовать?

// ArrayList: нужен быстрый доступ по индексу, редкие вставки
ArrayList students = new ArrayList<>(); // список студентов

// LinkedList: частые вставки в начало/конец (очередь, стек)
LinkedList taskQueue = new LinkedList<>(); // очередь задач

// HashMap: поиск по ключу, справочники
HashMap prices = new HashMap<>(); // прайс-лист

// HashSet: уникальные значения, проверка наличия
HashSet visitedURLs = new HashSet<>(); // посещённые страницы

Итого: что мы узнали

  • ✅ Коллекции — гибкие структуры данных с автоматическим изменением размера
  • ✅ ArrayList: динамический массив; быстрый доступ по индексу O(1)
  • ✅ LinkedList: двусвязный список; быстрые вставки в начало/конец O(1)
  • ✅ HashMap: словарь ключ→значение; поиск за O(1)
  • ✅ HashSet: только уникальные элементы; проверка за O(1)
  • ✅ Collections — утилиты: sort, shuffle, min, max, frequency
  • ✅ Iterator — безопасное удаление при обходе коллекции
  • ✅ Generics <String>: типизированные коллекции (ошибки на этапе компиляции)
  • ✅ removeIf() — элегантное условное удаление (Java 8+)