Как я могу инициализировать статическую карту? - PullRequest
1032 голосов
/ 03 февраля 2009

Как бы вы инициализировали статический Map в Java?

Метод первый: статический инициализатор
Способ второй: инициализатор экземпляра (анонимный подкласс) или же какой-то другой метод?

Каковы плюсы и минусы каждого?

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

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

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

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

Ответы [ 41 ]

1031 голосов
/ 03 февраля 2009

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

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

public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}
412 голосов
/ 31 августа 2011

Мне нравится Гуава способ инициализации статической неизменной карты:

static final Map<Integer, String> MY_MAP = ImmutableMap.of(
    1, "one",
    2, "two"
);

Как видите, он очень лаконичен (из-за удобных заводских методов в ImmutableMap).

Если вы хотите, чтобы на карте было более 5 записей, вы больше не можете использовать ImmutableMap.of(). Вместо этого попробуйте ImmutableMap.builder() по этим строкам:

static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
    .put(1, "one")
    .put(2, "two")
    // ... 
    .put(15, "fifteen")
    .build();

Чтобы узнать больше о преимуществах утилит неизменяемого сбора в Guava, см. Объясненные неизменяемые коллекции в Руководстве пользователя Guava .

(Подмножество) гуавы раньше называли Google Collections . Если вы еще не используете эту библиотеку в своем проекте Java, я настоятельно рекомендую попробовать! Guava быстро стал одним из самых популярных и полезных бесплатных сторонних библиотек для Java, так как пользователи SO соглашаются . (Если вы новичок в этом, за этой ссылкой есть несколько отличных учебных ресурсов.)


Обновление (2015) : Что касается Java 8 , я бы все равно использовал подход Guava, потому что он намного чище, чем все остальное. Если вам не нужна зависимость от Guava, рассмотрите простой старый метод инициализации 1038 *. Хак с двумерным массивом и Stream API довольно уродлив, если вы спросите меня, и становится еще более уродливым, если вам нужно создать карту, чьи ключи и значения не одного типа (например, Map<Integer, String> в вопрос).

Что касается будущего Guava в целом, что касается Java 8, Луи Вассерман сказал это в 2014 году, а [ обновление ] в 2016 году было объявлено, что Guava 21 потребует и будет должным образом поддерживать Java 8 .


Обновление (2016) : Как отметил Тагир Валеев , Java 9 , наконец, сделает это чистым, используя только чистый JDK, добавив удобство фабричных методов для коллекций:

static final Map<Integer, String> MY_MAP = Map.of(
    1, "one", 
    2, "two"
);
171 голосов
/ 04 февраля 2009

Я бы использовал:

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

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

Java 5 предоставляет этот более компактный синтаксис:

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};
90 голосов
/ 03 февраля 2009

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

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

 // later on...

 CONSTANT_MAP.put(3, "three"); // going to throw an exception!
59 голосов
/ 14 сентября 2014

Вот инициализатор статической карты 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]));

Edit (2): i_am_zero предлагает лучшую версию со смешанным типом, которая использует поток вызовов new SimpleEntry<>(k, v). Проверьте этот ответ: https://stackoverflow.com/a/37384773/3950982

48 голосов
/ 29 декабря 2015

В Java 9: ​​

private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");

Подробнее см. JEP 269 . JDK 9 достиг общая доступность в сентябре 2017 года.

29 голосов
/ 23 мая 2016

Java 9

Мы можем использовать Map.ofEntries как:

import static java.util.Map.entry;
private static final Map<Integer,String> map = Map.ofEntries(
        entry(1, "one"),
        entry(2, "two"),
        entry(3, "three"),
        entry(4, "four"),
        entry(5, "five"),
        entry(6, "six"),
        entry(7, "seven"),
        entry(8, "eight"),
        entry(9, "nine"),
        entry(10, "ten"));

Мы также можем использовать Map.of, как предложил Тагир в своем ответе здесь , но мы не можем иметь более 10 записей, используя Map.of.

Java 8 (Чистое решение)

Мы можем создать поток записей на карте. У нас уже есть две реализации Entry в java.util.AbstractMap, которые SimpleEntry и SimpleImmutableEntry . Для этого примера мы можем использовать первый как:

import java.util.AbstractMap.*;
private static final Map<Integer, String> myMap = Stream.of(
            new SimpleEntry<>(1, "one"),
            new SimpleEntry<>(2, "two"),
            new SimpleEntry<>(3, "three"),
            new SimpleEntry<>(4, "four"),
            new SimpleEntry<>(5, "five"),
            new SimpleEntry<>(6, "six"),
            new SimpleEntry<>(7, "seven"),
            new SimpleEntry<>(8, "eight"),
            new SimpleEntry<>(9, "nine"),
            new SimpleEntry<>(10, "ten"))
            .collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));
29 голосов
/ 19 декабря 2012

С Eclipse Collections будет работать все следующее:

import java.util.Map;

import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.impl.factory.Maps;

public class StaticMapsTest
{
    private static final Map<Integer, String> MAP =
        Maps.mutable.with(1, "one", 2, "two");

    private static final MutableMap<Integer, String> MUTABLE_MAP =
       Maps.mutable.with(1, "one", 2, "two");


    private static final MutableMap<Integer, String> UNMODIFIABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").asUnmodifiable();


    private static final MutableMap<Integer, String> SYNCHRONIZED_MAP =
        Maps.mutable.with(1, "one", 2, "two").asSynchronized();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").toImmutable();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP2 =
        Maps.immutable.with(1, "one", 2, "two");
}

Вы также можете статически инициализировать карты примитивов с помощью Eclipse Collections.

import org.eclipse.collections.api.map.primitive.ImmutableIntObjectMap;
import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
import org.eclipse.collections.impl.factory.primitive.IntObjectMaps;

public class StaticPrimitiveMapsTest
{
    private static final MutableIntObjectMap<String> MUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two");

    private static final MutableIntObjectMap<String> UNMODIFIABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asUnmodifiable();

    private static final MutableIntObjectMap<String> SYNCHRONIZED_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asSynchronized();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .toImmutable();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP2 =
            IntObjectMaps.immutable.<String>empty()
                    .newWithKeyValue(1, "one")
                    .newWithKeyValue(2, "two");
} 

Примечание: Я коммиттер Eclipse Collections

26 голосов
/ 03 февраля 2009

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

private static final Map<Integer, String> MY_MAP;
static
{
    Map<Integer, String>tempMap = new HashMap<Integer, String>();
    tempMap.put(1, "one");
    tempMap.put(2, "two");
    MY_MAP = Collections.unmodifiableMap(tempMap);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...