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


1132

Как бы вы инициализировали статический 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");
        }
    };
}

2
Для инициализации карты в Java 8: stackoverflow.com/a/37384773/1216775
akhil_mittal

2
Пожалуйста, никогда не используйте двойную инициализацию - это взлом, простой способ утечки памяти и другие проблемы.
dimo414

Java 9? Если количество записей <= 10, используйте Map.ofдругое Map.ofEntries, проверьте stackoverflow.com/a/37384773/1216775
akhil_mittal

Ответы:


1106

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

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

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);
    }
}

10
Это идиома, которую я использовал годами, и я никогда не видел, чтобы кто-то смотрел на нее. Я делаю то же самое для неизменяемых константных множеств и списков тоже.
jasonmp85

3
Как бы я обработал HashMap <String, String> с ключом String. Объект Map не позволяет мне иметь ключ String, поэтому я не могу использовать unmodifiableMap (). Я предполагаю, что приведение к HashMap также победит цель. Любые идеи?
Лука

30
@ Люк, я серьезно сомневаюсь, что у Android есть такое ограничение. Это не имеет никакого смысла. Быстрый поиск нашел этот вопрос здесь (и многие другие), который, кажется, подразумевает, что вы можете использовать ключ String для объекта Map в Android.
mluisbrown

11
Так что никто больше не потрудится исследовать, я могу подтвердить, что нет проблем с использованием ключа String для объекта Map на Android.
Джордан

11
Джордан: сейчас это старая тема, но я подозреваю, что @Luke пытался использовать строку в качестве ключа на карте с ключом другого типа, например Map <Integer, String>.
Мизерная переменная

445

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

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, рассмотрите простой старый метод init . Хак с двумерным массивом и 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"
);

21
Похоже, что наши коллеги-администраторы SO удалили почтенный вопрос «Самые полезные бесплатные сторонние библиотеки Java», на который я ссылался. :( Черт возьми.
Йоник

2
Я согласен, это самый хороший способ инициализации постоянной карты. Не только более читабельно, но и так как Collections.unmodifiableMap возвращает доступное только для чтения представление базовой карты (которое все еще может быть изменено).
crunchdog

11
Теперь я вижу удаленные вопросы (с 10k + rep), так что вот копия «Самые полезные бесплатные сторонние библиотеки Java» . Это только первая страница, но по крайней мере вы можете найти ресурсы Guava, упомянутые выше.
Jonik

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

2
JEP 186 до сих пор не закрыт, поэтому он может вводить новые функции, связанные с коллекционными литералами
cybersoft

182

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

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 является константой, я бы назвал ее постоянной

3
Из опций чистого JDK (без библиотек) мне это нравится больше всего, потому что определение карты явно связано с ее инициализацией. Также договорились о постоянном наименовании.
Джоник

Мне никогда не приходило в голову, что ты можешь сделать это.
romulusnr

181

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

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

46
Эта методика называется инициализацией двойной скобкой: stackoverflow.com/questions/1372113/… Это не специальный синтаксис Java 5, это просто уловка с анонимным классом с инициализатором экземпляра.
Джеспер

13
Быстрый вопрос относительно инициализации двойной скобки: при этом Eclipse выдает предупреждение об отсутствующем серийном идентификаторе. С одной стороны, я не понимаю, зачем нужен Serial ID в этом конкретном случае, но с другой стороны, я обычно не люблю подавлять предупреждения. Что вы думаете об этом?
nbarraille

8
@nbarraille Это потому что HashMap implements Serializable. Поскольку вы фактически создаете подкласс HashMap, используя этот «трюк», вы неявно создаете класс Serializable. И для этого вы должны предоставить serialUID.
никто не

5
Double brace initialization can cause memory leaks when used from a non-static context, because the anonymous class created will maintain a reference to the surrounding object. It has worse performance than regular initialization because of the additional class loading required. It can cause equals() comparisons to fail, if the equals() method does not accept subclasses as parameter. And finally, pre Java 9 it cannot be combined with the diamond operator, because that cannot be used with anonymous classes.- IntelliJ
Марк Джеронимус

3
@MarkJeronimus - предлагаемое использование является статическим контекстом. Производительность может быть хуже, но не так заметно при работе с предположительно небольшим количеством статически определенных карт. HashMap.equalsопределяется в любом подклассе Map AbstractMapи работает с ним , поэтому здесь это не касается. Оператор с бриллиантами раздражает, но, как уже упоминалось, теперь решен.
Жюль

95

Одним из преимуществ второго метода является то, что вы можете обернуть его, 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!

3
Разве вы не можете легко сделать это в первом методе, переместив оператор new в статический блок {} и обернув его?
Патрик

2
В любом случае, я бы переместил вызов конструктора в инициализированный static. Все остальное просто выглядит странно.
Том Хотин - tackline

2
Любая идея, какой удар производительности может быть от использования анонимного класса в отличие от конкретного класса?
Кип

62

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

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


7
Я взял на себя смелость добавить версию, которая эквивалентна вопросу и другим ответам: инициализировать карту, ключи и значения которой имеют другой тип (поэтому String[][]не будет делать, Object[][]это необходимо). ИМХО, этот подход уродлив (даже в большей степени с кастами) и его трудно запомнить; не буду использовать это сам.
Джоник

57

Map.of в Java 9+

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

См. JEP 269 для деталей. JDK 9 поступил в продажу в сентябре 2017 года.


7
Или, если вы хотите более 10 пар ключ-значение, вы можете использоватьMap.ofEntries
Жека Козлов

8
Это чисто и все, пока вы не поймете, как это было реализовано
середина

Тьфу, это так грустно - похоже, он поддерживает только 10 записей, после чего вам нужно использовать ofEntries. Ламе.
Сомайя Кумбера

2
Чистота реализации в JDK не должна иметь значения, если она работает и удовлетворяет условиям контракта. Как и любой черный ящик, детали реализации всегда могут быть исправлены в будущем, если это действительно необходимо ...
vikingsteve

@mid Это единственный типобезопасный способ сделать это на Java.
Люк Хатчисон

44

Java 9

Мы можем использовать Map.ofEntries, позвонив, Map.entry( k , v )чтобы создать каждую запись.

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));

2
new SimpleEntry<>()Путь гораздо менее читабельным , чем статические put(): /
Danon

32

С 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.

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


1
Мне бы очень хотелось, чтобы Eclipse Collections была библиотекой коллекций по умолчанию для Java. Я наслаждаюсь этим гораздо больше, чем Guava + JCL.
Кенни Кейсон

29

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

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);
}

1
В какой ситуации вы бы использовали анонимный подкласс для инициализации hashmap?
Dogbane

6
Никогда не инициализировать коллекцию.
eljenso

Не могли бы вы объяснить, почему использование статического инициализатора является лучшим выбором, чем создание анонимного подкласса?
Леба Лев

3
@rookie Есть несколько причин, приведенных в других ответах в пользу статической инициализации. Цель здесь состоит в том, чтобы инициализировать, так зачем вводить подклассы, за исключением, может быть, сохранения нескольких нажатий клавиш? (Если вы хотите сэкономить на нажатиях клавиш, Java определенно не является хорошим выбором в качестве языка программирования.) Одно практическое правило, которое я использую при программировании на Java: подкласс как можно меньше (и никогда, когда его можно разумно избежать).
eljenso

@eljenso - причина, по которой я обычно предпочитаю синтаксис подкласса, заключается в том, что он помещает инициализацию в строку, где он принадлежит . Второй лучший выбор - вызвать статический метод, который возвращает инициализированную карту. Но я боюсь, что посмотрю на ваш код и потрачу несколько секунд, чтобы понять, откуда взялся MY_MAP, и это время, которое я не хочу тратить впустую. Любое улучшение читабельности является бонусом, а последствия для производительности минимальны, поэтому мне кажется, что это лучший вариант.
Жюль

18

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

Обновление: эта библиотека теперь называется Guava .


17

Мне нравится анонимный класс, потому что с ним легко иметь дело:

public static final Map<?, ?> numbers = Collections.unmodifiableMap(new HashMap<Integer, String>() {
    {
        put(1, "some value");
                    //rest of code here
    }
});

12
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);
    }
}

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

public class Test {

    public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
        {
            put(1, "one");
            put(2, "two");
        }
    });
}

И предлагается использовать unmodifiableMap для констант, иначе он не может рассматриваться как константа.


10

Я мог бы настоятельно рекомендовать стиль «инициализация двойной скобкой» вместо стиля статического блока.

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

Но то, что я больше рассматриваю, - это удобочитаемость кода и удобство сопровождения. С этой точки зрения, я считаю, что двойная скобка - лучший стиль кода, а не статический метод.

  1. Элементы вложенные и встроенные.
  2. Это больше ОО, а не процедурный.
  3. влияние на производительность очень мало и может быть проигнорировано.
  4. Лучшая поддержка структуры IDE (а не много анонимных статических блоков {})
  5. Вы сохранили несколько строк комментариев, чтобы привести их отношения.
  6. Предотвратите возможную утечку элемента / лидерство экземпляра неинициализированного объекта от исключения и оптимизатора байт-кода.
  7. Не беспокойтесь о порядке выполнения статического блока.

Кроме того, если вы знаете GC анонимного класса, вы всегда можете преобразовать его в обычный HashMap с помощью new HashMap(Map map).

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


8

Как обычно, apache-commons имеет правильный метод MapUtils.putAll (Map, Object []) :

Например, чтобы создать цветную карту:

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });

Я включаю Apache Commons во все сборки, поэтому, к сожалению, при отсутствии метода Arrays.asMap( ... )в простой Java я думаю, что это лучшее решение. Изобретать колесо обычно глупо. Очень слабым недостатком является то, что с дженериками потребуется непроверенное преобразование.
Майк Грызун

Версия @mikerodent 4.1 является общей: общедоступная статическая <K, V> Map <K, V> putAll (окончательная карта Map <K, V>, конечный массив Object [])
agad

Tx ... да, я использую 4.1, но мне все еще нужно SuppressWarnings( unchecked )в Eclipse с такой строкой, какMap<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
Майк Грызун

@mikerodent не так ли из-за Object [] [] ? Смотрите обновленную версию - у меня нет предупреждений в Eclipse.
Агад

Как странно ... даже когда я иду, String[][]я получаю "предупреждение"! И, конечно, это работает, только если вы Kи один Vи тот же класс. Я так понимаю, вы (понятно) не установили «непроверенное преобразование» в «Игнорировать» в настройках Eclipse?
Майк Грызун

7

Вот мой любимый, когда я не хочу (или не могу) использовать Guava ImmutableMap.of(), или если мне нужен изменчивый Map:

public static <A> Map<String, A> asMap(Object... keysAndValues) {
    return new LinkedHashMap<String, A>() {{
        for (int i = 0; i < keysAndValues.length - 1; i++) {
            put(keysAndValues[i].toString(), (A) keysAndValues[++i]);
        }
    }};
}

Он очень компактен и игнорирует случайные значения (т. Е. Конечный ключ без значения).

Применение:

Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));

7

Если вы хотите неизменяемую карту, наконец, Java 9 добавил классный метод фабрики ofдля Mapинтерфейса. Аналогичный метод добавлен в Set, List, а также.

Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");


6

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

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

Первый совет

Первый совет: вы можете сделать локальную ссылку на карту и дать ей короткое имя:

private static final Map<Integer, String> myMap = new HashMap<>();
static {
    final Map<Integer, String> m = myMap; // Use short name!
    m.put(1, "one"); // Here referencing the local variable which is also faster!
    m.put(2, "two");
    m.put(3, "three");
}

Второй совет

Второй совет: вы можете создать вспомогательный метод для добавления записей; Вы также можете сделать этот вспомогательный метод общедоступным, если хотите:

private static final Map<Integer, String> myMap2 = new HashMap<>();
static {
    p(1, "one"); // Calling the helper method.
    p(2, "two");
    p(3, "three");
}

private static void p(Integer k, String v) {
    myMap2.put(k, v);
}

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

Третий совет

Третий совет заключается в том, что вы можете создать многократно используемый вспомогательный класс, похожий на конструктор, с заполненной функциональностью. Это действительно простой 10-строчный вспомогательный класс, который безопасен для типов:

public class Test {
    private static final Map<Integer, String> myMap3 = new HashMap<>();
    static {
        new B<>(myMap3)   // Instantiating the helper class with our map
            .p(1, "one")
            .p(2, "two")
            .p(3, "three");
    }
}

class B<K, V> {
    private final Map<K, V> m;

    public B(Map<K, V> m) {
        this.m = m;
    }

    public B<K, V> p(K k, V v) {
        m.put(k, v);
        return this; // Return this for chaining
    }
}

5

Создаваемый вами анонимный класс хорошо работает. Однако вы должны знать, что это внутренний класс, и поэтому он будет содержать ссылку на экземпляр окружающего класса. Таким образом, вы обнаружите, что не можете делать с ним определенные вещи (используя XStream для одного). Вы получите очень странные ошибки.

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

РЕДАКТИРОВАТЬ: правильно отметил в комментариях, что это статический класс. Очевидно, я не читал это достаточно внимательно. Однако мои комментарии все еще применимы к анонимным внутренним классам.


3
В данном конкретном случае это статично, поэтому нет внешнего экземпляра.
Том Хотин - tackline

Возможно, XStream не должен пытаться сериализовать подобные вещи (это статично. Зачем вам нужно сериализовать статическую переменную?)
jasonmp85

5

Если вам нужно что-то лаконичное и относительно безопасное, вы можете просто перенести проверку типов во время компиляции во время выполнения:

static final Map<String, Integer> map = MapUtils.unmodifiableMap(
    String.class, Integer.class,
    "cat",  4,
    "dog",  2,
    "frog", 17
);

Эта реализация должна отлавливать любые ошибки:

import java.util.HashMap;

public abstract class MapUtils
{
    private MapUtils() { }

    public static <K, V> HashMap<K, V> unmodifiableMap(
            Class<? extends K> keyClazz,
            Class<? extends V> valClazz,
            Object...keyValues)
    {
        return Collections.<K, V>unmodifiableMap(makeMap(
            keyClazz,
            valClazz,
            keyValues));
    }

    public static <K, V> HashMap<K, V> makeMap(
            Class<? extends K> keyClazz,
            Class<? extends V> valClazz,
            Object...keyValues)
    {
        if (keyValues.length % 2 != 0)
        {
            throw new IllegalArgumentException(
                    "'keyValues' was formatted incorrectly!  "
                  + "(Expected an even length, but found '" + keyValues.length + "')");
        }

        HashMap<K, V> result = new HashMap<K, V>(keyValues.length / 2);

        for (int i = 0; i < keyValues.length;)
        {
            K key = cast(keyClazz, keyValues[i], i);
            ++i;
            V val = cast(valClazz, keyValues[i], i);
            ++i;
            result.put(key, val);
        }

        return result;
    }

    private static <T> T cast(Class<? extends T> clazz, Object object, int i)
    {
        try
        {
            return clazz.cast(object);
        }
        catch (ClassCastException e)
        {
            String objectName = (i % 2 == 0) ? "Key" : "Value";
            String format = "%s at index %d ('%s') wasn't assignable to type '%s'";
            throw new IllegalArgumentException(String.format(format, objectName, i, object.toString(), clazz.getSimpleName()), e);
        }
    }
}

4

В Java 8 я использовал следующий шаблон:

private static final Map<String, Integer> MAP = Stream.of(
    new AbstractMap.SimpleImmutableEntry<>("key1", 1),
    new AbstractMap.SimpleImmutableEntry<>("key2", 2)
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Это не самое лаконичное и немного окольное, но

  • это не требует ничего вне java.util
  • он безопасен и легко приспосабливается к различным типам ключей и значений.

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

4

Если вам нужно добавить только одно значение на карту, вы можете использовать Collections.singletonMap :

Map<K, V> map = Collections.singletonMap(key, value)


4

Ваш второй подход (инициализация двойной скобки) считается анти-паттерном , поэтому я бы остановился на первом подходе.

Другой простой способ инициализации статической карты - использование этой служебной функции:

public static <K, V> Map<K, V> mapOf(Object... keyValues) {
    Map<K, V> map = new HashMap<>(keyValues.length / 2);

    for (int index = 0; index < keyValues.length / 2; index++) {
        map.put((K)keyValues[index * 2], (V)keyValues[index * 2 + 1]);
    }

    return map;
}

Map<Integer, String> map1 = mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = mapOf("key1", "value1", "key2", "value2");

Примечание: Java 9вы можете использовать Map.of


3

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

public class MyClass {
    private static final Map<Integer, String> myMap = prepareMap();

    private static Map<Integer, String> prepareMap() {
        Map<Integer, String> hashMap = new HashMap<>();
        hashMap.put(1, "one");
        hashMap.put(2, "two");

        return hashMap;
    }
}

3

Я не видел подход, который я использую (и который мне понравился), размещенный в каких-либо ответах, поэтому вот он:

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

вместо этого я предпочитаю инициализацию, которая выглядит так:

map(
    entry("keyA", "val1"),
    entry("keyB", "val2"),
    entry("keyC", "val3")
);

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

 public static <K,V> Map<K,V> map(Map.Entry<K, ? extends V>... entries)
 public static <K,V> Map.Entry<K,V> entry(K key, V val)

(вы можете использовать «import static», чтобы избежать добавления префикса имени метода)

Я нашел полезным предоставить аналогичные статические методы для других коллекций (list, set, sortedSet, sortedMap и т. Д.)

Это не так хорошо, как инициализация объекта json, но это шаг в этом направлении, что касается читабельности.


3

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

К счастью, можно приблизить поведение литералов карты в Java, используя фабричные методы .

Например:

public class LiteralMapFactory {

    // Creates a map from a list of entries
    @SafeVarargs
    public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) {
        LinkedHashMap<K, V> map = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }
    // Creates a map entry
    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
        return new AbstractMap.SimpleEntry<>(key, value);
    }

    public static void main(String[] args) {
        System.out.println(mapOf(entry("a", 1), entry("b", 2), entry("c", 3)));
    }
}

Вывод:

{a = 1, b = 2, c = 3}

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


2

JEP 269 предоставляет некоторые удобные фабричные методы для API коллекций. Методы этой фабрики отсутствуют в текущей версии Java, которая составляет 8, но запланированы для выпуска Java 9.

Ибо Mapесть два заводских метода: ofа ofEntries. Используя of, вы можете передавать чередующиеся пары ключ / значение. Например, чтобы создать Mapподобное {age: 27, major: cs}:

Map<String, Object> info = Map.of("age", 27, "major", "cs");

В настоящее время существует десять перегруженных версий для of, поэтому вы можете создать карту, содержащую десять пар ключ / значение. Если вам не нравится это ограничение или переменный ключ / значения, вы можете использовать ofEntries:

Map<String, Object> info = Map.ofEntries(
                Map.entry("age", 27),
                Map.entry("major", "cs")
);

Оба ofи ofEntriesвернут неизменяемость Map, поэтому вы не сможете изменить их элементы после постройки. Вы можете опробовать эти функции, используя JDK 9 Ранний доступ .


2

Ну ... я люблю перечисления;)

enum MyEnum {
    ONE   (1, "one"),
    TWO   (2, "two"),
    THREE (3, "three");

    int value;
    String name;

    MyEnum(int value, String name) {
        this.value = value;
        this.name = name;
    }

    static final Map<Integer, String> MAP = Stream.of( values() )
            .collect( Collectors.toMap( e -> e.value, e -> e.name ) );
}

2

Я прочитал ответы и решил написать свой собственный конструктор карт. Не стесняйтесь копировать-вставлять и наслаждаться.

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

/**
 * A tool for easy creation of a map. Code example:<br/>
 * {@code MapBuilder.of("name", "Forrest").and("surname", "Gump").build()}
 * @param <K> key type (inferred by constructor)
 * @param <V> value type (inferred by constructor)
 * @author Vlasec (for http://stackoverflow.com/a/30345279/1977151)
 */
public class MapBuilder <K, V> {
    private Map<K, V> map = new HashMap<>();

    /** Constructor that also enters the first entry. */
    private MapBuilder(K key, V value) {
        and(key, value);
    }

    /** Factory method that creates the builder and enters the first entry. */
    public static <A, B> MapBuilder<A, B> mapOf(A key, B value) {
        return new MapBuilder<>(key, value);
    }

    /** Puts the key-value pair to the map and returns itself for method chaining */
    public MapBuilder<K, V> and(K key, V value) {
        map.put(key, value);
        return this;
    }

    /**
     * If no reference to builder is kept and both the key and value types are immutable,
     * the resulting map is immutable.
     * @return contents of MapBuilder as an unmodifiable map.
     */
    public Map<K, V> build() {
        return Collections.unmodifiableMap(map);
    }
}

РЕДАКТИРОВАТЬ: В последнее время я продолжаю находить публичный статический метод ofдовольно часто, и мне это нравится. Я добавил его в код и сделал конструктор приватным, переключившись на статический шаблон метода фабрики.

РЕДАКТИРОВАТЬ 2: Еще недавно я больше не любил вызывать статический метод of, так как он выглядит довольно плохо при использовании статического импорта. mapOfВместо этого я переименовал его, чтобы сделать его более подходящим для статического импорта.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.