Поиск…


Вступление

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

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

замечания

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

  • Данные сохраняются на карте в парах ключ / значение.
  • Карта может содержать только одну запись для определенного ключа. Если карта содержит запись с определенным ключом, и вы пытаетесь сохранить вторую запись с тем же ключом, вторая запись заменит первую. Другими словами, это изменит значение, связанное с ключом.
  • Карты обеспечивают быструю работу, чтобы проверить, существует ли ключ на карте, чтобы получить значение, связанное с ключом, и удалить пару ключ / значение.

Наиболее часто используемой реализацией карты является HashMap . Он хорошо работает с ключами, которые являются строками или цифрами.

Обычные карты, такие как HashMap, неупорядочены. Итерация через пары ключ / значение может возвращать отдельные записи в любом порядке. Если вам необходимо выполнять итерацию через записи карты контролируемым образом, вы должны посмотреть на следующее:

  • Сортированные карты, такие как TreeMap, будут перебирать ключи в натуральном порядке (или в порядке, который вы можете указать, предоставив Comparator ). Например, сортированная карта, использующая числа как ключи, должна была бы перебирать свои записи в числовом порядке.

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

Добавить элемент

  1. прибавление
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element."); 
System.out.println(map.get(1));

Выход: First element.

  1. Override
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
map.put(1, "New element.");
System.out.println(map.get(1));

Выход: New element.

HashMap качестве примера используется HashMap . Могут использоваться и другие реализации, реализующие интерфейс Map .

Добавить несколько элементов

Мы можем использовать V put(K key,V value) :

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

String currentVal;
Map<Integer, String> map = new TreeMap<>();
currentVal = map.put(1, "First element.");
System.out.println(currentVal);// Will print null
currentVal = map.put(2, "Second element.");
System.out.println(currentVal); // Will print null yet again    
currentVal = map.put(2, "This will replace 'Second element'");
System.out.println(currentVal); // will print Second element.
System.out.println(map.size()); // Will print 2 as key having
// value 2 was replaced.

Map<Integer, String> map2 = new HashMap<>();
map2.put(2, "Element 2");
map2.put(3, "Element 3");

map.putAll(map2);

System.out.println(map.size());  

Выход:

3

Чтобы добавить много элементов, вы можете использовать внутренние классы следующим образом:

Map<Integer, String> map = new HashMap<>() {{
    // This is now an anonymous inner class with an unnamed instance constructor
    put(5, "high");
    put(4, "low");
    put(1, "too slow");
}};

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

static Map<Integer, String> map = new HashMap<>();

static {
    // Now no inner classes are created so we can avoid memory leaks
    put(5, "high");
    put(4, "low");
    put(1, "too slow");
}

Приведенный выше пример делает карту статической. Его также можно использовать в нестационарном контексте, удалив все вхождения static .

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

another.putAll(one);

Использование методов сопоставления по умолчанию из Java 8

Примеры использования методов по умолчанию, введенных в Java 8 в интерфейсе карты

  1. Использование getOrDefault

Возвращает значение, отображаемое на ключ, или если ключ отсутствует, возвращает значение по умолчанию

Map<Integer, String> map = new HashMap<>();
map.put(1, "First element");
map.get(1);                                 // => First element
map.get(2);                                 // => null
map.getOrDefault(2, "Default element");     // => Default element
  1. Использование forEach

Позволяет выполнять операцию, указанную в «действии» для каждой записи карты

  Map<Integer, String> map = new HashMap<Integer, String>();
  map.put(1, "one");
  map.put(2, "two");
  map.put(3, "three");
  map.forEach((key, value) -> System.out.println("Key: "+key+ " :: Value: "+value));

   // Key: 1 :: Value: one
   // Key: 2 :: Value: two
   // Key: 3 :: Value: three
  1. Использование replaceAll

Будет заменено новым значением, только если присутствует ключ

 Map<String, Integer> map = new HashMap<String, Integer>();
 map.put("john", 20);
 map.put("paul", 30);
 map.put("peter", 40);
 map.replaceAll((key,value)->value+10);   //{john=30, paul=40, peter=50}
  1. Использование putIfAbsent

Пара ключей-значений добавляется к карте, если ключ отсутствует или отображается на нуль

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("john", 20);
    map.put("paul", 30);
    map.put("peter", 40);
    map.putIfAbsent("kelly", 50);     //{john=20, paul=30, peter=40, kelly=50}
  1. Использование remove

    Удаляет ключ только в том случае, если он связан с заданным значением

     Map<String, Integer> map = new HashMap<String, Integer>();
     map.put("john", 20);
     map.put("paul", 30);
     map.put("peter", 40);
     map.remove("peter",40); //{john=30, paul=40}
    
  2. Использование замены

    Если ключ присутствует, значение заменяется новым значением. Если ключ отсутствует, ничего не делает.

Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.replace("peter",50); //{john=20, paul=30, peter=50}
map.replace("jack",60); //{john=20, paul=30, peter=50}
  1. Использование computeIfAbsent

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

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("john", 20);
    map.put("paul", 30);
    map.put("peter", 40);
    map.computeIfAbsent("kelly", k->map.get("john")+10); //{john=20, paul=30, peter=40, kelly=30}
    map.computeIfAbsent("peter", k->map.get("john")+10); //{john=20, paul=30, peter=40, kelly=30} //peter already present
  1. Использование computeIfPresent

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

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("john", 20);
    map.put("paul", 30);
    map.put("peter", 40);
    map.computeIfPresent("kelly", (k,v)->v+10); //{john=20, paul=30, peter=40} //kelly not present
    map.computeIfPresent("peter", (k,v)->v+10); //{john=20, paul=30, peter=50} // peter present, so increase the value 
  1. Использование вычисления

Этот метод заменяет значение ключа на новое вычисляемое значение

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("john", 20);
    map.put("paul", 30);
    map.put("peter", 40);
    map.compute("peter", (k,v)->v+50); //{john=20, paul=30, peter=90} //Increase the value 
  1. Использование слияния

Добавляет пару ключ-значение к карте, если ключ отсутствует, или значение для ключа равно null Заменяет значение на вновь вычисленное значение, если ключ присутствует. Ключ удаляется с карты, если новое вычисляемое значение равно null

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("john", 20);
    map.put("paul", 30);
    map.put("peter", 40);
    
    //Adds the key-value pair to the map, if key is not present or value for the key is null
    map.merge("kelly", 50 , (k,v)->map.get("john")+10); // {john=20, paul=30, peter=40, kelly=50}
    
    //Replaces the value with the newly computed value, if the key is present
    map.merge("peter", 50 , (k,v)->map.get("john")+10); //{john=20, paul=30, peter=30, kelly=50}
   
    //Key is removed from the map , if new value computed is null
    map.merge("peter", 30 , (k,v)->map.get("nancy")); //{john=20, paul=30, kelly=50}

Очистить карту

Map<Integer, String> map = new HashMap<>();

map.put(1, "First element.");
map.put(2, "Second element.");
map.put(3, "Third element.");

map.clear();

System.out.println(map.size());   // => 0

Итерация через содержимое Карты

Карты предоставляют методы, позволяющие вам получить доступ к ключам, значениям или парам ключ-значение карты в виде коллекций. Вы можете проходить через эти коллекции. Например, на следующей карте:

Map<String, Integer> repMap = new HashMap<>();
repMap.put("Jon Skeet", 927_654);
repMap.put("BalusC", 708_826);
repMap.put("Darin Dimitrov", 715_567);

Итерация с помощью клавиш карты:

for (String key : repMap.keySet()) {
    System.out.println(key);
}

Печать:

Дарин Димитров
Джон Скит
BalusC

keySet() предоставляет ключи карты в виде Set . Set используется, поскольку ключи не могут содержать повторяющиеся значения. Итерация через набор дает каждый ключ по очереди. HashMaps не заказываются, поэтому в этом примере ключи могут быть возвращены в любом порядке.

Итерирование по значениям карты:

for (Integer value : repMap.values()) {
    System.out.println(value);
}

Печать:

715567
927654
708826

values() возвращает значения карты как Collection . Итерация через коллекцию дает каждое значение по очереди. Опять же, значения могут быть возвращены в любом порядке.

Итерация через ключи и значения вместе

for (Map.Entry<String, Integer> entry : repMap.entrySet()) {
    System.out.printf("%s = %d\n", entry.getKey(), entry.getValue());
}

Печать:

Дарин Димитров = 715567
Jon Skeet = 927654
BalusC = 708826

entrySet() возвращает коллекцию объектов Map.Entry . Map.Entry предоставляет доступ к ключу и значению для каждой записи.

Объединение, объединение и составление карт

Используйте putAll чтобы поместить каждого члена одной карты в другую. Ключи, уже присутствующие на карте, будут перезаписаны соответствующими значениями.

Map<String, Integer> numbers = new HashMap<>();
numbers.put("One", 1)
numbers.put("Three", 3)
Map<String, Integer> other_numbers = new HashMap<>();
other_numbers.put("Two", 2)
other_numbers.put("Three", 4)

numbers.putAll(other_numbers)

Это дает следующее отображение в numbers :

"One" -> 1
"Two" -> 2
"Three" -> 4 //old value 3 was overwritten by new value 4

Если вы хотите комбинировать значения вместо их перезаписи, вы можете использовать Map.merge , добавленный в Java 8, который использует предоставленную пользователем BiFunction для объединения значений для дубликатов ключей. merge работает с отдельными ключами и значениями, поэтому вам нужно будет использовать цикл или Map.forEach . Здесь мы объединяем строки для дубликатов ключей:

for (Map.Entry<String, Integer> e : other_numbers.entrySet())
    numbers.merge(e.getKey(), e.getValue(), Integer::sum);
//or instead of the above loop
other_numbers.forEach((k, v) -> numbers.merge(k, v, Integer::sum));

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

mapA.forEach((k, v) ->
    mapB.merge(k, v, (v1, v2) ->
        {throw new AssertionError("duplicate values for key: "+k);}));

Составление карты <X, Y> и карты <Y, Z> для получения карты <X, Z>

Если вы хотите составить два сопоставления, вы можете сделать это следующим образом

    Map<String, Integer> map1 = new HashMap<String, Integer>();
    map1.put("key1", 1);
    map1.put("key2", 2);
    map1.put("key3", 3);
    
    Map<Integer, Double> map2 = new HashMap<Integer, Double>();
    map2.put(1, 1.0);
    map2.put(2, 2.0);
    map2.put(3, 3.0);

    Map<String, Double> map3 = new new HashMap<String, Double>();
    map1.forEach((key,value)->map3.put(key,map2.get(value)));

Это дает следующее отображение

    "key1" -> 1.0
    "key2" -> 2.0
    "key3" -> 3.0

Проверьте, существует ли ключ

Map<String, String> num = new HashMap<>();
num.put("one", "first");

if (num.containsKey("one")) {
    System.out.println(num.get("one")); // => first
}

Карты могут содержать нулевые значения

Для карт нужно быть уверенным, чтобы не путать «содержащий ключ» с «имеющим ценность». Например, HashMap s может содержать нуль, что означает, что следующее абсолютно нормальное поведение:

Map<String, String> map = new HashMap<>();
map.put("one", null);
if (map.containsKey("one")) {
    System.out.println("This prints !"); // This line is reached 
}
if (map.get("one") != null) {
    System.out.println("This is never reached !"); // This line is never reached 
}

Более формально, нет гарантии, что map.contains(key) <=> map.get(key)!=null

Итерирование картографических записей Эффективно

В этом разделе приведены коды и контрольные показатели для десяти уникальных реализаций примеров, которые перебирают записи Map<Integer, Integer> и генерируют сумму значений Integer . Все примеры имеют алгоритмическую сложность Θ(n) , однако критерии все еще полезны для того, чтобы дать представление о том, какие реализации более эффективны в среде «реального мира».

  1. Реализация с использованием Iterator с Map.Entry
    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<Integer, Integer> pair = it.next();
        sum += pair.getKey() + pair.getValue();
    }
  1. Реализация с использованием for с Map.Entry
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        sum += pair.getKey() + pair.getValue();
    }
  1. Реализация с использованием Map.forEach (Java 8+)
    map.forEach((k, v) -> sum[0] += k + v);
  1. Реализация с использованием Map.keySet for
    for (Integer key : map.keySet()) {
        sum += key + map.get(key);
    }
  1. Реализация с использованием Map.keySet с Iterator
    Iterator<Integer> it = map.keySet().iterator();
    while (it.hasNext()) {
        Integer key = it.next();
        sum += key + map.get(key);
    }
  1. Реализация с использованием for с Iterator и Map.Entry
    for (Iterator<Map.Entry<Integer, Integer>> entries = 
             map.entrySet().iterator(); entries.hasNext(); ) {
        Map.Entry<Integer, Integer> entry = entries.next();
        sum += entry.getKey() + entry.getValue();
    }
  1. Реализация с использованием Stream.forEach (Java 8+)
    map.entrySet().stream().forEach(e -> sum += e.getKey() + e.getValue());
  1. Реализация с использованием Stream.forEach с Stream.parallel (Java 8+)
    map.entrySet()
       .stream()
       .parallel()
       .forEach(e -> sum += e.getKey() + e.getValue());
  1. Внедрение с использованием IterableMap из коллекций Apache
    MapIterator<Integer, Integer> mit = iterableMap.mapIterator();
    while (mit.hasNext()) {
        sum += mit.next() + it.getValue();
    }
  1. Реализация с использованием MutableMap из коллекций Eclipse
     mutableMap.forEachKeyValue((key, value) -> {
         sum += key + value;
     });

Тесты производительности ( код доступен в Github )
Условия тестирования: Windows 8.1 64-бит, Intel i7-4790 3,60 ГГц, 16 ГБ

  1. Средняя производительность 10 испытаний (100 элементов) Лучшее: 308 ± 21 нс / оп

    Benchmark                           Score     Error  Units
    test3_UsingForEachAndJava8            308  ±     21  ns/op
    test10_UsingEclipseMutableMap         309  ±      9  ns/op
    test1_UsingWhileAndMapEntry           380  ±     14  ns/op
    test6_UsingForAndIterator             387  ±     16  ns/op
    test2_UsingForEachAndMapEntry         391  ±     23  ns/op
    test7_UsingJava8StreamAPI             510  ±     14  ns/op
    test9_UsingApacheIterableMap          524  ±      8  ns/op
    test4_UsingKeySetAndForEach           816  ±     26  ns/op
    test5_UsingKeySetAndIterator          863  ±     25  ns/op
    test8_UsingJava8StreamAPIParallel    5552  ±    185  ns/op
    
  2. Средняя производительность 10 испытаний (10000 элементов) Лучшее: 37,606 ± 0,790 мкс / оп

    Benchmark                           Score     Error  Units
    test10_UsingEclipseMutableMap       37606  ±    790  ns/op
    test3_UsingForEachAndJava8          50368  ±    887  ns/op
    test6_UsingForAndIterator           50332  ±    507  ns/op
    test2_UsingForEachAndMapEntry       51406  ±   1032  ns/op
    test1_UsingWhileAndMapEntry         52538  ±   2431  ns/op
    test7_UsingJava8StreamAPI           54464  ±    712  ns/op
    test4_UsingKeySetAndForEach         79016  ±  25345  ns/op
    test5_UsingKeySetAndIterator        91105  ±  10220  ns/op
    test8_UsingJava8StreamAPIParallel  112511  ±    365  ns/op
    test9_UsingApacheIterableMap       125714  ±   1935  ns/op
    
  3. Средняя производительность 10 испытаний (100000 элементов) Лучшее: 1184,767 ± 332,968 мкс / оп

    Benchmark                            Score       Error  Units
    test1_UsingWhileAndMapEntry       1184.767  ±  332.968  μs/op
    test10_UsingEclipseMutableMap     1191.735  ±  304.273  μs/op
    test2_UsingForEachAndMapEntry     1205.815  ±  366.043  μs/op
    test6_UsingForAndIterator         1206.873  ±  367.272  μs/op
    test8_UsingJava8StreamAPIParallel 1485.895  ±  233.143  μs/op
    test5_UsingKeySetAndIterator      1540.281  ±  357.497  μs/op
    test4_UsingKeySetAndForEach       1593.342  ±  294.417  μs/op
    test3_UsingForEachAndJava8        1666.296  ±  126.443  μs/op
    test7_UsingJava8StreamAPI         1706.676  ±  436.867  μs/op
    test9_UsingApacheIterableMap      3289.866  ± 1445.564  μs/op
    
  4. Сравнение вариаций производительности в зависимости от размера карты

График производительности

       x: Size of Map
    f(x): Benchmark Score (μs/op)

                 100      600     1100     1600     2100
        ---------------------------------------------------
        10  |   0.333    1.631    2.752    5.937    8.024
         3  |   0.309    1.971    4.147    8.147   10.473
         6  |   0.372    2.190    4.470    8.322   10.531
         1  |   0.405    2.237    4.616    8.645   10.707
Tests    2  |   0.376    2.267    4.809    8.403   10.910
 f(x)    7  |   0.473    2.448    5.668    9.790   12.125
         9  |   0.565    2.830    5.952    13.22   16.965
         4  |   0.808    5.012    8.813    13.939  17.407
         5  |   0.81     5.104    8.533    14.064  17.422
         8  |   5.173   12.499   17.351    24.671  30.403

Использовать пользовательский объект в качестве ключа

Прежде чем использовать свой собственный объект в качестве ключа, вы должны переопределить метод hashCode () и equals () вашего объекта.

В простом случае у вас будет что-то вроде:

class MyKey {
    private String name;
    MyKey(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {            
        if(obj instanceof MyKey) {
            return this.name.equals(((MyKey)obj).name);
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        return this.name.hashCode();
    }
}

hashCode определит, какой хэш-ведро принадлежит ключу, и equals будет решать, какой объект внутри этого хэш-ведра.

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

Использование HashMap

HashMap - это реализация интерфейса Map, который предоставляет структуру данных для хранения данных в парах Key-Value.

1. Объявление HashMap

Map<KeyType, ValueType> myMap = new HashMap<KeyType, ValueType>();

KeyType и ValueType должны быть допустимыми типами на Java, такими как - String, Integer, Float или любой пользовательский класс, например Employee, Student и т. Д.

Например: Map<String,Integer> myMap = new HashMap<String,Integer>();

2. Ввод значений в HashMap.

Чтобы поместить значение в HashMap, мы должны вызвать метод put в объекте HashMap, передав параметры Key и Value as.

myMap.put("key1", 1);
myMap.put("key2", 2);

Если вы вызываете метод put с ключом, который уже существует в Map, метод переопределяет его значение и возвращает старое значение.

3. Получение значений из HashMap.

Для получения значения из HashMap вам нужно вызвать метод get , передав ключ в качестве параметра.

myMap.get("key1");    //return 1 (class Integer)

Если вы передадите ключ, который не существует в HashMap, этот метод вернет значение null

4. Проверьте, находится ли ключ на карте или нет.

myMap.containsKey(varKey);

5. Проверьте, находится ли значение на карте или нет.

myMap.containsValue(varValue);

Вышеуказанные методы возвращают boolean значение true или false, если ключ, значение существует на карте или нет.

Создание и инициализация карт

Вступление

Maps хранят пары ключ / значение, где каждый ключ имеет связанное значение. Учитывая определенный ключ, карта может быстро найти связанное значение.

Maps , также известные как ассоциированный массив, представляют собой объект, который хранит данные в виде ключей и значений. В Java карты представлены с использованием интерфейса Map, который не является расширением интерфейса коллекции.

  • Путь 1: -

     /*J2SE < 5.0*/
     Map map = new HashMap();
     map.put("name", "A");
     map.put("address", "Malviya-Nagar");
     map.put("city", "Jaipur");
     System.out.println(map);
    
  • Путь 2: -

     /*J2SE 5.0+ style (use of generics):*/
     Map<String, Object> map = new HashMap<>();
     map.put("name", "A");
     map.put("address", "Malviya-Nagar");
     map.put("city", "Jaipur");
     System.out.println(map);
    
  • Путь 3: -

      Map<String, Object> map = new HashMap<String, Object>(){{
          put("name", "A");
          put("address", "Malviya-Nagar");
          put("city", "Jaipur");
      }};
      System.out.println(map);
    
  • Путь 4: -

      Map<String, Object> map = new TreeMap<String, Object>();
          map.put("name", "A");
          map.put("address", "Malviya-Nagar");
          map.put("city", "Jaipur");
      System.out.println(map);
    
  • Путь 5: -

      //Java 8
      final Map<String, String> map =
          Arrays.stream(new String[][] {
              { "name", "A" }, 
              { "address", "Malviya-Nagar" }, 
              { "city", "jaipur" },
          }).collect(Collectors.toMap(m -> m[0], m -> m[1]));
      System.out.println(map);
    
  • Путь 6: -

      //This way for initial a map in outside the function
      final static Map<String, String> map;
      static
      {
          map = new HashMap<String, String>();
          map.put("a", "b");
          map.put("c", "d");
      }
    
  • Путь 7: - Создание неизменной карты с одним ключом.

        //Immutable single key-value map
        Map<String, String> singletonMap = Collections.singletonMap("key", "value");
    

    Обратите внимание, что эту карту невозможно изменить .

    Любые attemts для изменения карты приведут к бросанию UnsupportedOperationException.

        //Immutable single key-value pair
        Map<String, String> singletonMap = Collections.singletonMap("key", "value");
        singletonMap.put("newKey", "newValue"); //will throw UnsupportedOperationException
        singletonMap.putAll(new HashMap<>()); //will throw UnsupportedOperationException
        singletonMap.remove("key"); //will throw UnsupportedOperationException
        singletonMap.replace("key", "value", "newValue"); //will throw UnsupportedOperationException
        //and etc
    


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow