JavaRush /Java блог /Random UA /9 головних питань про Map в Java
Treefeed
21 рівень

9 головних питань про Map в Java

Стаття з групи Random UA
Нагадаємо, що Map - це структуровані дані, що складаються з набору пар ключ-значення, і кожен ключ може використовуватися лише один раз на одній Map. Ця тема розкриває 9 основних питань про використання Map у Java та її втілених (імплементованих) класах. Для простоти, я буду використовувати в прикладах узагальнення . Тому, я писатиму просто Map, не конкретизуючи специфікатор Map. Але можна вважати, що обидва значення K і V можна порівняти, що означає K розширює Comparable і V так само розширює Comparable .9 головних питань про Map в Java - 1

0. Звернення Map у List

У Java, інтерфейс Map пропонує три види колекцій: набір ключів, набір значень та набір ключ-значення. Всі вони можуть бути звернені до List за допомогою конструктора або методу addAll(). Наступна вирізка коду демонструє, як зробити ArrayList з Map.
//аркуш ключів
List keyList = new ArrayList(Map.keySet());
//аркуш значень
List valueList = new ArrayList(Map.valueSet());
//аркуш ключ-значення
List entryList = new ArrayList(Map.entrySet());

1. Пройтися за всіма значеннями в Map

Прохід по кожній парі ключ-значення - базова, основна процедура проходу по Map. У Java кожна пара зберігається в полі Map званому Map.Entry . Map.entrySet()повертає набір ключ-значень, тому найефективнішим способом пройтися по всіх значеннях Map буде:
for(Entry entry: Map.entrySet()) {
  //отримати ключ
  K key = entry.getKey();
  //отримати значення
  V value = entry.getValue();
}
Також ми можемо використовувати Iterator, особливо у версіях молодших JDK 1.5
Iterator itr = Map.entrySet().iterator();
while(itr.hasNext()) {
  Entry entry = itr.next();
  //отримати ключ
  K key = entry.getKey();
  //отримати значення
  V value = entry.getValue();
}

2. Упорядкування Map за ключами

Упорядкування Map за ключами ще одна процедура, що часто зустрічається. Перший спосіб: додати Map.Entry до списку і впорядкувати з використанням компаратора, що сортує за значеннями.
List list = new ArrayList(Map.entrySet());
Collections.sort(list, new Comparator() {

  @Override
  public int compare(Entry e1, Entry e2) {
    return e1.getKey().compareTo(e2.getKey());
  }
});
Інший спосіб: використовувати SortedMap , яка до всього ще й вибудовує свої ключі по порядку. Але всі ключі при цьому повинні втілювати Comparable або прийматися компаратором. Один із імплементованих класів SortedMap- TreeMap . Її конструктор приймає компаратор. Наступний код показує як перетворити звичайну Mapна впорядковану.
SortedMap sortedMap = new TreeMap(new Comparator() {

  @Override
  public int compare(K k1, K k2) {
    return k1.compareTo(k2);
  }

});
sortedMap.putAll(Map);

3. Упорядкування Map за значеннями

Додавання Map до списку та подальше сортування працюють і в даному випадку, але потрібно цього разу брати Entry.getValue(). Код нижче майже такий самий, як і раніше.
List list = new ArrayList(Map.entrySet());
Collections.sort(list, new Comparator() {

  @Override
  public int compare(Entry e1, Entry e2) {
    return e1.getValue().compareTo(e2.getValue());
  }

});
Ми все ще можемо використовувати SortedMapв цьому випадку, але якщо значення унікальні. У такому випадку, ви можете звернути пару ключ-значення до значення-ключ. Це рішення має суворе обмеження, і не рекомендується мною.

4. Ініціалізація статичної/незмінної Map

Коли ви хочете, щоб Map залишалася незмінною, хорошим способом скопіюватиме її в незмінну (immutable) Map. Така захисна техніка програмування допоможе вам створити не тільки безпечну для використання, але й потокобезпечну Map. Для ініціалізації статичної/незмінної Map ми можемо використовувати ініціалізатор static(див. нижче). Проблема даного коду в тому, що, не дивлячись на оголошення Map як static final, ми все ще можемо працювати з нею після ініціалізації, наприклад Test.Map.put(3,"three");. Тож це не справжня незмінність. Для створення незмінної Map з використанням статичного ініціалізатора нам потрібен супер анонімний клас, який ми додамо в незмінну Map на останньому кроці ініціалізації. Будь ласка, подивіться на другу частину коду. Коли буде викинутоUnsupportedOperationException , якщо ви запустите Test.Map.put(3,"three");.
public class Test {

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

  private static final Map Map;
  static {
    Map aMap = new HashMap();
    aMap.put(1, "one");
    aMap.put(2, "two");
    Map = Collections.unmodifiableMap(aMap);
  }
}
Бібліотека Guava також підтримує різні способи ініціалізації статичних та постійних колекцій. Щоб дізнатися більше про переваги утиліти Guava для постійних колекцій, зверніться до розділу Постійні колекції в Інструкції Guava .

5. Різниця між HashMap, TreeMap, та Hashtable

Є три основні втілення інтерфейсу Map Java: HashMap , TreeMap , і Hashtable . Головні відмінності полягають у наступному:
  • Порядок проходу . HashMap і HashTable не дають гарантій упорядкованості в Map; зокрема, вони не гарантують, що порядок залишиться тим самим протягом часу. Але TreeMapвпорядковуватиме всі значення в "природному порядку" ключів або за компаратором.
  • Допустимі пари ключ-значення. HashMapдозволяє мати ключ null та значення null. HashTableне дозволяє ключ null чи значення null. Якщо TreeMapвикористовує природний порядок або компаратор не дозволяє використовувати ключ null, буде викинуто виняток.
  • Синхронізація . Тільки HashTableсинхронізована, решта — ні. Але, "якщо потокобезпечне втілення не потрібне, рекомендується використовувати HashMapзамість HashTable".
Докладніше порівняння
.                       | HashMap | HashTable | TreeMap
-------------------------------------------------------

Упорядочивание          |нет      |нет        | да
null в ключ-значення    | да-да   | нет-нет   | нет-да
синхронизировано        | нет     | да        | нет
производительность      | O(1)    | O(1)      | O(log n)
воплощение              | корзины | корзины   | красно-чёрное дерево
Докладніше про відносини HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap .

6. Map з реверсивним пошуком/переглядом

Іноді, нам потрібен набір пар ключ-ключ, що має на увазі значення так само унікальні, як і ключі (патерн один до одного). Така сталість дозволяє створити "інвертований перегляд/пошук" Map. Тобто ми можемо знайти ключ за його значенням. Така структура даних називається двонаправлена ​​Map , яка, на жаль, не підтримується JDK. Обидві Apache Common Collections та Guava пропонують втілення двонаправленої Map, звані BidiMap та BiMap, відповідно. Обидві вводять обмеження, яке задає відповідність 1:1 між ключами та значеннями.

7. Поверхнева копія Map

Майже всі, якщо не всі, Map Java містить конструктор копіювання інший Map. Але процедуру копіювання не синхронізовано. Що означає, коли один потік копіює Map, інший може змінити її структуру. Для запобігання раптовій розсинхронізації копіювання один з них повинен використовувати в такому випадку Collections.synchronizedMap().
Map copiedMap = Collections.synchronizedMap(Map);
Інший цікавий спосіб поверхневого копіювання - використання методу clone(). Але він не рекомендується навіть творцем фреймворку колекцій Java, Джошуа Блохом. У суперечці " Конструктор копіювання проти клонування ", він займає позицію: Цитата: "Я часто наводжу публічний метод clone в конкретних класах, оскільки люди очікують їх там побачити. ... це ганьба, що Клонування зламано, але це сталося. ... Клонування це слабке місце, та я думаю люди повинні бути попереджені про його обмеження." З цієї причини, я навіть не показую вам, як використовувати метод clone()копіювання Map

8. Створення порожній Map

Якщо Mapнезмінна, використовуйте:
Map = Collections.emptyMap();
Або використовуйте будь-яке інше втілення. Наприклад:
Map = new HashMap();
КІНЕЦЬ
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ