Как инициализировать статическую 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");
}
};
}
Инициализатор экземпляра - это просто синтаксический сахар в данном случае, верно? Я не понимаю, зачем вам нужен дополнительный анонимный класс только для инициализации. И он не будет работать, если создаваемый класс является конечным.
Вы можете создать неизменяемую карту, используя статический инициализатор:
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);
}
}
Мне нравится фейхоа способ инициализации статических, неизменяемых карте:
static final Map<Integer, String> MY_MAP = ImmutableMap.of(
1, "one",
2, "two"
);
Как вы видите, это's очень лаконичным (из-за удобного фабричные методы в ImmutableMap
).
Если вы хотите иметь более 5 записей, вы больше не можете использовать ImmutableMap.из()
. Вместо этого попробуйте ImmutableMap.Строитель()
вдоль этих линий:
static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
.put(1, "one")
.put(2, "two")
// ...
.put(15, "fifteen")
.build();
Чтобы узнать больше о преимуществах гуава'с неизменным коммунальные услуги сбора, см. immutable коллекции разъяснения руководства пользователя гуава.
(Подмножество) гуава называли коллекции в Google. Если вы еще't использование данной библиотеки в Java-проект, я сильно рекомендую попробовать его! Гуава быстро стал одним из самых популярных и полезных бесплатно 3-й партии библиотеки для Java, как коллег, так что пользователи согласны]5. (Если вы новичок в этом, есть некоторые отличные ресурсы для обучения за этой ссылке.)
Обновление (2015): по в Java 8, Ну, я бы все-таки использовать гуава подход, поскольку он намного чище, чем все остальное. Если вы Don'т хотим, зависимость гуава, рассмотрим простой старый метод init. Рубить с двумерный массив и поток по API очень некрасиво, если вы спросите меня, и становится уродливее, если вам нужно создать карту, ключи и значения не одного типа (например, карты<целое число, строка>` в этот вопрос).
Как для будущих гуавы в целом, в отношении к Java 8, Луи Вассерман сказал еще в 2014 году, и [обновление] в 2016 году было объявлено, что гуава 21 потребует и правильно поддержка Java 8.
Обновление (2016): как Тагир Валеев отмечает, Ява 9, наконец, сделает уборку не используя ничего, кроме чистой версии JDK, путем добавления удобство метода фабрики для коллекции:
static final Map<Integer, String> MY_MAP = Map.of(
1, "one",
2, "two"
);
Я хотел бы использовать:
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);
}
}
Одно из преимуществ второго метода заключается в том, что вы можете обернуть его 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!
Здесь'ы в 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]));
Редактировать: для инициализации карты<целое число, строка>` как в вопрос, вы'd нужен что-то вроде этого:
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, который использует поток новых SimpleEntry<>(к, в) призывы. Проверить этот ответ: https://stackoverflow.com/a/37384773/3950982
В Java 9:
private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");
См. Сеп 269 для деталей. С JDK 9, достигнутым доступность в сентябре 2017 года.
Мы можем использовать карты.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"));
Мы также можем использовать карту.окак полагает Тагир в ответ [здесь][4] но мы не можем иметь больше, чем 10 записей, используя
карту.из`.
Мы можем создать поток записи в карте. У нас уже есть две реализации Entry
в `Ява.утиль.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));
С коллекциях затмение, все следующее будет работать:
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");
}
Вы также можете статически инициализировать примитивные карты с коллекции затмение.
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");
}
Примечание: Я коммиттер для коллекций затмение
Я никогда не хотел создать анонимный подкласс в этой ситуации. Статические инициализаторы одинаково хорошо работать, если вы хотите сделать карту неизменяемым например:
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);
}
Может быть, это'ы интересно почитать Гугл коллекции, например, видео, что они на своей странице. Они предоставляют различные способы инициализации карты и наборы, а также неизменяемые коллекции.
Обновление: эта библиотека теперь называется фейхоа.
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 для константы другие мудрые он может'т рассматриваться как постоянные.
Я мог бы настоятельно рекомендовать в "двойной дубль инициализации" в стиле за статический блок стиль.
Кто-то может прокомментировать, что они Дон'т, как анонимный класс, издержки, производительность и т. д.
Но я больше считаю это читабельность кода и ремонтопригодность. В этой точке зрения я стою двойной расчалка лучшего стиля кода, а затем статический метод.
Кроме того, вы осведомлены ГК анонимного класса, вы всегда можете преобразовать его в нормальное хранилище HashMap с помощью новый HashMap(карты)
.
Вы можете сделать это пока вы не столкнулись с другой проблемой. Если вы делаете, вы должны использовать другой стиль программирования (например, отсутствие статический, класса фабрики) для него.
Как обычно Апач-Коммонс имеет правильный метод MapUtils.putAll(карта<К в>, Объект[]):
Например, чтобы создать цветовую карту:
Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
{"RED", "#FF0000"},
{"GREEN", "#00FF00"},
{"BLUE", "#0000FF"}
});
Если вы хотите карту неизменяемым, наконец, в Java 9 добавлен классный метод фабрики о
на карте
интерфейс. Подобный метод добавляется в набор, а также список.
Карты<строку, строку> unmodifiableMap = карте.на (на"ключ1-то", то "значение1" и "Ну ключ2-то", то "значение2-то");`
Здесь's мой любимый, когда я Дон'т хотите (или не можете) использовать гуава'ImmutableMap ых.из (), или если мне нужно изменчивое "карта":
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]);
}
}};
}
Это's очень компактный, и он игнорирует бродячих значений (т. е. Последний ключ без значения).
Использование:
Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));
Я предпочитаю использовать статический инициализатор, чтобы избежать создания анонимных классов (которые в дальнейшем не будут иметь цели), поэтому я'Лл советы список инициализации, с помощью статического инициализатора. Все перечисленные решения / советы являются типобезопасными.
Примечание: этот вопрос не'т сказать что-нибудь о карте неизменяемым, так что я оставлю это, но знаю, что это может легко быть сделано с коллекции.unmodifiableMap(карты)
.
Первый конец
1-ый совет, что вы можете сделать локальную ссылку на карту и дать ему короткое имя:
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");
}
Второй совет
2-й совет, что вы можете создать вспомогательный метод, чтобы добавить записи; вы можете также сделать это вспомогательный метод открытыми, если вы хотите:
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
. Чтобы сделать его повторного использования, мы могли бы сделать самой карте параметр вспомогательный метод, но тогда код инициализации не будет короче.
Третий конец
3-й совет, что вы можете создать многоразовый строителя-как вспомогательный класс при заполнении функциональность. Это очень простой, 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
}
}
Анонимный класс вы'вновь создавая хорошо работает. Однако вы должны знать, что это внутренний класс и как таковая, она'МР содержат ссылку на окружающие экземпляра класса. Так что вы'll найти вы можете'т делать определенные вещи с ним (используя кинокомпании xStream за один). Вы'll получить некоторые очень странные ошибки.
Сказав, что, так долго, как вы'вэ известно, то такой подход-это нормально. Я использую его большую часть времени для инициализации всевозможные коллекции в лаконичной моды.
Редактировать: справедливо отметили в комментариях, что это статический класс. Очевидно, я этого't читать это достаточно близко. Однако мои комментарии делать все-таки обратиться в анонимные внутренние классы.
Если вы хотите что-то немногословен и относительно безопасная, вы можете просто сместить время компиляции проверка типа времени выполнения:
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);
}
}
}