12

Как инициализировать статическую Map?

21

Вопрос на StackOverflow:

Как инициализировать статическую Map в Java?

Я рассмотрел несколько подходов:

  1. Статический инициализатор
  2. Инициализатор экземпляра (анонимный подкласс) или же какой-то другой метод?

Каковы плюсы и минусы каждого из этих методов?

Вот пример, иллюстрирующий два метода:

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

public class Test {
    private static final Map<Integer, String> myMap = new HashMap<>();
    static {
        myMap.put(1, "one");
        myMap.put(2, "two");
    }

    private static final Map<Integer, String> myMap2 = new HashMap<>() {
        {
            put(1, "one");
            put(2, "two");
        }
    };
}

Буду признателен за пояснения и примеры различных подходов к инициализации статической Map!

5 ответ(ов)

12

Да, вы правы: инициализатор экземпляра в данном случае действительно является синтаксическим сахаром. Если вам нужно просто выполнить инициализацию, то использование дополнительного анонимного класса может показаться избыточным. К тому же, если создаваемый класс является финальным, то это приведет к ошибке, так как финальные классы не могут быть подклассами.

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

public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = new HashMap<>();
        aMap.put(1, "один");
        aMap.put(2, "два");
        myMap = Collections.unmodifiableMap(aMap);
    }
}

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

2

Я бы использовал следующий код:

public class Test {
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}
  1. Это позволяет избежать анонимного класса, который, по моему мнению, является плохим стилем и лишним.
  2. Создание карты становится более явным.
  3. Карта получается неизменяемой.
  4. Так как MY_MAP является константой, я бы назвал её в соответствии с константами.
1

В Java 5 была введена более компактная синтаксическая конструкция для инициализации коллекций. Ниже приведён пример, как можно инициализировать экземпляр HashMap с помощью анонимного внутреннего класса:

static final Map<String, String> FLAVORS = new HashMap<String, String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};

В этом коде создаётся анонимный класс, унаследованный от HashMap, и в нём в блоке инициализации выполняются действия по добавлению элементов в карту. Этот подход позволяет вам инициализировать Map с несколькими элементами, не создавая отдельные вызовы метода put после создания объекта. Однако стоит отметить, что использование такого синтаксиса создаёт дополнительный класс в памяти, поэтому стоит учитывать это при выборе подхода для инициализации коллекций.

1

Одним из преимуществ второго метода является возможность обернуть его в Collections.unmodifiableMap(), что гарантирует, что коллекция не будет изменена в дальнейшем:

private static final Map<Integer, String> CONSTANT_MAP = 
    Collections.unmodifiableMap(new HashMap<Integer, String>() {{ 
        put(1, "one");
        put(2, "two");
    }});

// Позже...

CONSTANT_MAP.put(3, "three"); // будет выброшено исключение!

Таким образом, если вы попытаетесь внести изменения в CONSTANT_MAP, это приведет к выбросу исключения UnsupportedOperationException, что защищает вашу коллекцию от случайных изменений.

0

Вот однострочное инициализатор для статической карты на Java 8:

private static final Map<String, String> EXTENSION_TO_MIMETYPE =
    Arrays.stream(new String[][] {
        { "txt", "text/plain" }, 
        { "html", "text/html" }, 
        { "js", "application/javascript" },
        { "css", "text/css" },
        { "xml", "application/xml" },
        { "png", "image/png" }, 
        { "gif", "image/gif" }, 
        { "jpg", "image/jpeg" },
        { "jpeg", "image/jpeg" }, 
        { "svg", "image/svg+xml" },
    }).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1]));

Если вам нужно инициализировать Map<Integer, String>, как в вопросе, можно использовать следующее:

static final Map<Integer, String> MY_MAP = Arrays.stream(new Object[][]{
        {1, "one"},
        {2, "two"},
}).collect(Collectors.toMap(kv -> (Integer) kv[0], kv -> (String) kv[1]));

Кроме того, следует отметить, что есть более удобный вариант от i_am_zero, который использует поток с вызовами new SimpleEntry<>(k, v). Ознакомьтесь с его ответом: https://stackoverflow.com/a/37384773/3950982

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь