Как инициализировать статическую Map?
Вопрос на StackOverflow:
Как инициализировать статическую Map
в Java?
Я рассмотрел несколько подходов:
- Статический инициализатор
- Инициализатор экземпляра (анонимный подкласс) или же какой-то другой метод?
Каковы плюсы и минусы каждого из этих методов?
Вот пример, иллюстрирующий два метода:
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 ответ(ов)
Да, вы правы: инициализатор экземпляра в данном случае действительно является синтаксическим сахаром. Если вам нужно просто выполнить инициализацию, то использование дополнительного анонимного класса может показаться избыточным. К тому же, если создаваемый класс является финальным, то это приведет к ошибке, так как финальные классы не могут быть подклассами.
Вы можете создать неизменяемую карту и с помощью статического инициализатора, как вы показали в своем примере:
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);
}
}
Такой подход позволяет избежать использования дополнительных классов и делает код более чистым и понятным. Возможно, стоит использовать статический инициализатор, если вы хотите создать неизменяемые коллекции, особенно если вам не нужны ни экземпляры класса, ни сложная логика инициализации.
Я бы использовал следующий код:
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);
}
}
- Это позволяет избежать анонимного класса, который, по моему мнению, является плохим стилем и лишним.
- Создание карты становится более явным.
- Карта получается неизменяемой.
- Так как MY_MAP является константой, я бы назвал её в соответствии с константами.
В 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
после создания объекта. Однако стоит отметить, что использование такого синтаксиса создаёт дополнительный класс в памяти, поэтому стоит учитывать это при выборе подхода для инициализации коллекций.
Одним из преимуществ второго метода является возможность обернуть его в 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
, что защищает вашу коллекцию от случайных изменений.
Вот однострочное инициализатор для статической карты на 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
Как напрямую инициализировать HashMap (в литеральном виде)?
Эффективный способ итерации по каждой записи в Java Map?
Инициализация ArrayList в одну строчку
Сортировка Map<Key, Value> по значениям
Как инициализировать значения HashSet при создании?