Коллекции

Коллекции позволяют хранить несколько значений одного типа. Отличие от массивов:

  1. В коллекциях больше возможностей: можно вставлять, удалять элементы и многое другое.
  2. Хранить можно только значения объектных типов. Т.е. невозможно создать коллекцию для типа, например, int. Если числа все-таки надо хранить, то храниться они будут в типе Integer.
  3. Массивы встроены в язык синтаксически (операция взятия индекса с помощью квадратных скобок)
  4. Массивы часто эффективней коллекций.

Старайтесь не смешивать в программе массивы и коллекции.

Как создать коллекцию?

Коллекции бывают разных видов, самые распространные - это List и Set (списки и множества). Что это значит, см. далее.

List<Integer> - тип, коллекция, точнее, список Integer.

Set<String> - множество строк.

List<Path> - список путей к файлам.

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

Если вы забудете указать тип в угловых скобках, вы увидите предупреждения “Unchecked cast/…”.

Списки и множества

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

Создание коллекций

  1. Функция List.of или Set.of
     List<Integer> l1 = List.of(1, 2, 3);
     Set<String> s1 = Set.of("abc", "xy");
    

    они создают коллекцию из указанных элементов. (В Python l1 = [1, 2, 3] и s1 = {"abc", "xy"}).

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

  2. Явное создание:
     List<Integer> l2 = new ArrayList<Integer>();
     Set<String> s2 = new HashSet<String>();
    

    При создании указываем конкретный тип списка или множества, который мы хотим. Бывает ArrayList, LinkedList, …, LinkedHashSet, …

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

    Как укоротить создание списка:

     //можно справа не писать содержимое угловых скобок
     List<Integer> l2 = new ArrayList<>();
     Set<String> s2 = new HashSet<>();
    

    Или

     //можно писать var:
     var l2 = new ArrayList<Integer>();
     var s2 = new HashSet<String>();
    

Действия с коллекциями

действия для любых коллекций

Полный список действий

  1. l1.add(42), s1.add("abc") - добавить элемент. У списков это добавление в конец.

  2. l1.addAll(s2) - добавить в одну коллекцию все элементы другой.

  3. l1.contains(42) - проверка, есть ли элемент в коллекции. Для множеств эта операция обычно работает быстро.

  4. l1.clear() - очистить.

  5. l1.remove(42) - удалить определенный элемент

  6. Цикл перебора элементов коллекции:

     for (int x : l1)
         System.out.println(x);
    

    Заведена переменная x того же типа, что и содержимое коллекции. Ей последовательно присваиваются все элементы коллекции. Для списка порядок перебора совпадает с порядком элементов.

    Для множеств.

    • Если это HashSet, то порядок перебора произвольный. Это множество надо использовать, если вам нужно просто множество.
    • TreeSet, то порядок перебора - по возрастанию.
    • LinkedHashSet, то порядок перебора совпадает с порядком добавления.

Действия для Списков

Полный список действий

  1. l1.add(1, 42) Добавить элемент 42 по индексу 1
  2. l1.get(1) Узнать элемент по индекусу 1. (в python: l1[1])
  3. l1.remove(1) Удалить элемент по индексу

Ассоциативные массивы, тип Map<K, V>

Соответствует dict в python.

Обычный массив int[] a = {10, 20, 30};

a[ ]
  0   -> 10
  1   -> 20
  2   -> 30

Ассоциативный массив

m[ ]
  "cat" -> 3
  "dog" -> 3
  "rabbit" -> 6

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

В python это делается так:

m = {}
m["cat"] = 3
m["dog"] = 3
m["rabbit"] = 6

В Java:

1.

    Map<String, Integer> m = Map.of(
        "cat", 3,
        "dog", 3,
        "rabbit", 6
    );   
обязательно указываем оба типа: тип ключей (String) и тип значений
(Integer). Этим способом нельзя указать больше 10 элементов.\
Получающийся `Map` нельзя изменить

2.

    Map<String, Integer> m = new HashMap<>();
    m.put("cat", 3);
    m.put("dog", 3);
    m.put("rabbit", 6);
метод `put` добавляет в ассоциативный массив запись.
`HashMap`, `LinkedHashMap`, `TreeMap` аналогичны соответствующим
множествам в плане того, как они хранят ключи. Т.е. от выбора Map
зависит порядок перебора ключей.

действия над ассоциативными массивами

Полный список действий

  1. m.put(key, value), соответствует m[key] = value.
  2. m.get(key) возвращает значение для данного ключа или null, если ключа нет.
  3. m.getOrDefault(key, default) возвращает значение для ключа key, но если его нет, возвращает default.
  4. m.containsKey(key) узнать, содержится ли ключ.
  5. m.remove(key) удалить запись с данным ключом.

Перебор элементов ассоциативного массива

  1. m.keySet() - множество всех ключей
    for (String key : m.keySet())
     System.out.println(key + " -> " + m.get(key));
    
  2. m.values() - коллекция всех значений 1.
    m.forEach((key, value) -> {
     //с отступом в 4 пробела пишем код
     System.out.println(key + " -> " + value);
    });
    

    Результат аналогичен первому варианту перебора. Новый синтаксис со стрелочкой, это лямбда выражения, см. след. семестр.

  3. m.entrySet() - множество записей, пар ключ-значение, тип Map.Entry<String, Integer>.