«1. Обзор

Мы часто используем карты для хранения набора пар ключ-значение. Затем, в какой-то момент, нам часто нужно перебирать их.

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

2. Оптимизация итерации карты

Предположим, что у нас есть карта названий книг с именем автора в качестве ключа:

Map<String, String> map = new HashMap<>();

map.put("Robert C. Martin", "Clean Code");
map.put("Joshua Bloch", "Effective Java");

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

2.1. Использование Map.keySet

Сначала рассмотрим следующее:

for (String key : bookMap.keySet()) {
    System.out.println("key: " + key + " value: " + bookMap.get(key));
}

Здесь цикл перебирает набор ключей. Для каждого ключа мы получаем соответствующее значение с помощью Map.get. Хотя это очевидный способ использования всех записей на карте, он требует двух операций для каждой записи — одной для получения следующего ключа и одной для поиска значения с помощью get.

Если нам нужны только ключи на карте, keySet — хороший вариант. Однако есть более быстрый способ получить как ключи, так и значения.

2.2. Использование Map.entrySet вместо этого

Давайте перепишем нашу итерацию, чтобы использовать entrySet:

for (Map.Entry<String, String> book: bookMap.entrySet()) {
    System.out.println("key: " + book.getKey() + " value: " + book.getValue());
}

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

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

3. Работа с кортежами

Кортеж — это структура данных с фиксированным числом и порядком элементов. Мы можем думать о Map.Entry как о кортеже, который хранит два элемента — ключ и значение. Однако, поскольку Map.Entry — это интерфейс, нам требуется класс реализации. В этом разделе мы рассмотрим одну реализацию, предоставляемую JDK: AbstractMap.SimpleEntry.

3.1. Создание кортежа

Сначала рассмотрим класс Book:

public class Book {
    private String title;
    private String author;

    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }
    ...

Затем давайте создадим кортеж Map.Entry с ISBN в качестве ключа и объектом Book в качестве значения:

Map.Entry<String, Book> tuple;

Наконец, давайте создадим экземпляр нашего кортежа с помощью AbstractMap.SimpleEntry:

tuple = new AbstractMap.SimpleEntry<>("9780134685991", new Book("Effective Java 3d Edition", "Joshua Bloch"));

3.2. Создание упорядоченного списка кортежей

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

Во-первых, мы определим наш список кортежей:

List<Map.Entry<String, Book>> orderedTuples = new ArrayList<>();

Во-вторых, давайте добавим несколько записей в наш список:

orderedTuples.add(new AbstractMap.SimpleEntry<>("9780134685991", 
  new Book("Effective Java 3d Edition", "Joshua Bloch")));
orderedTuples.add(new AbstractMap.SimpleEntry<>("9780132350884", 
  new Book("Clean Code","Robert C Martin")));

3.3. Сравнение с картой

Чтобы сравнить различия с картой, давайте добавим новую запись с уже существующим ключом:

orderedTuples.add(new AbstractMap.SimpleEntry<>("9780132350884", 
  new Book("Clean Code", "Robert C Martin")));

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

for (Map.Entry<String, Book> tuple : orderedTuples) {
    System.out.println("key: " + tuple.getKey() + " value: " + tuple.getValue());
}

Наконец, давайте посмотрим на результат:

key: 9780134685991 value: Book{title='Effective Java 3d Edition', author='Joshua Bloch'}
key: 9780132350884 value: Book{title='Clean Code', author='Robert C Martin'}
key: 9780132350884 value: Book{title='Clean Code', author='Robert C Martin'}

Обратите внимание, что у нас могут быть повторяющиеся ключи, в отличие от базовой карты, где каждый ключ должен быть уникальным. Это связано с тем, что мы использовали реализацию List для хранения наших объектов SimpleEntry, что означает, что все объекты независимы друг от друга.

3.4. Списки объектов Entry

Следует отметить, что цель Entry не в том, чтобы действовать как общий кортеж. Классы библиотеки часто предоставляют для этой цели общий класс Pair.

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

4. Заключение

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

Затем мы рассмотрели, как Map.Entry можно использовать в качестве кортежа.

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

Как всегда, код примера доступен на GitHub.