«1. Обзор

Иногда предпочтительнее запретить модификации java.util.Map, например совместное использование данных только для чтения между потоками. Для этой цели мы можем использовать либо неизменяемую карту, либо неизменяемую карту.

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

2. Немодифицируемая и неизменяемая

Немодифицируемая карта — это просто оболочка над модифицируемой картой, и она не позволяет изменять ее напрямую:

Map<String, String> mutableMap = new HashMap<>();
mutableMap.put("USA", "North America");

Map<String, String> unmodifiableMap = Collections.unmodifiableMap(mutableMap);
assertThrows(UnsupportedOperationException.class,
  () -> unmodifiableMap.put("Canada", "North America"));

Но базовая изменяемая карта по-прежнему может быть изменена, и изменения также отражаются в неизменяемой карте:

mutableMap.remove("USA");
assertFalse(unmodifiableMap.containsKey("USA"));
		
mutableMap.put("Mexico", "North America");
assertTrue(unmodifiableMap.containsKey("Mexico"));

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

3. Неизменяемая карта Guava

Guava предоставляет неизменяемые версии каждого java.util.Map с использованием ImmutableMap. Он генерирует исключение UnsupportedOperationException всякий раз, когда мы пытаемся изменить его.

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

Теперь мы обсудим различные способы создания экземпляров ImmutableMap.

3.1. Использование метода copyOf()

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

ImmutableMap<String, String> immutableMap = ImmutableMap.copyOf(mutableMap);
assertTrue(immutableMap.containsKey("USA"));

Его нельзя изменить прямо или косвенно:

assertThrows(UnsupportedOperationException.class,
  () -> immutableMap.put("Canada", "North America"));
		
mutableMap.remove("USA");
assertTrue(immutableMap.containsKey("USA"));
		
mutableMap.put("Mexico", "North America");
assertFalse(immutableMap.containsKey("Mexico"));

3.2. Использование метода builder()

Мы также можем использовать метод ImmutableMap.builder() для создания копии всех записей, как на исходной карте.

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

ImmutableMap<String, String> immutableMap = ImmutableMap.<String, String>builder()
  .putAll(mutableMap)
  .put("Costa Rica", "North America")
  .build();
assertTrue(immutableMap.containsKey("USA"));
assertTrue(immutableMap.containsKey("Costa Rica"));

Так же, как и в предыдущем примере, мы не можем прямо или косвенно изменить его:

assertThrows(UnsupportedOperationException.class,
  () -> immutableMap.put("Canada", "North America"));
		
mutableMap.remove("USA");
assertTrue(immutableMap.containsKey("USA"));
		
mutableMap.put("Mexico", "North America");
assertFalse(immutableMap.containsKey("Mexico"));

~~ ~ 3.3. Использование метода of()

Наконец, мы можем использовать метод ImmutableMap.of() для создания неизменяемой карты с набором записей, предоставляемых на лету. Он поддерживает не более пяти пар ключ/значение:

ImmutableMap<String, String> immutableMap
  = ImmutableMap.of("USA", "North America", "Costa Rica", "North America");
assertTrue(immutableMap.containsKey("USA"));
assertTrue(immutableMap.containsKey("Costa Rica"));

Мы также не можем изменить его:

assertThrows(UnsupportedOperationException.class,
  () -> immutableMap.put("Canada", "North America"));

4. Заключение

В этой быстрой статье мы обсудили различия между немодифицируемой картой и неизменяемой картой. Карта.

Мы также рассмотрели различные способы создания ImmutableMap в Guava.

И, как всегда, полные примеры кода доступны на GitHub.