Как я могу инициализировать статическую карту? - 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 ]

16 голосов
/ 04 февраля 2009

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

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

16 голосов
/ 19 сентября 2013

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

public static final Map<?, ?> numbers = Collections.unmodifiableMap(new HashMap<Integer, String>() {
    {
        put(1, "some value");
                    //rest of code here
    }
});
11 голосов
/ 03 июня 2010
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 голосов
/ 12 декабря 2010

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

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

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

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

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

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

8 голосов
/ 08 сентября 2015

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

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

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });
7 голосов
/ 15 сентября 2016

Вот мой любимый, когда я не хочу (или не могу) использовать Guava's 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 голосов
/ 26 ноября 2017

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

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

5 голосов
/ 22 апреля 2014

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

Примечание: В этом вопросе ничего не говорится о том, чтобы сделать карту не изменяемой, поэтому я опущу это, но знаю, что это легко сделать с помощью 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 голосов
/ 03 февраля 2009

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

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

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

4 голосов
/ 02 августа 2013

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

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);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...