Java Language
Карты
Поиск…
Вступление
Интерфейс java.util.Map представляет собой сопоставление между ключами и их значениями. Карта не может содержать дубликаты ключей; и каждый ключ может отображать не более одного значения.
Поскольку Map
является интерфейсом, вам необходимо создать конкретную реализацию этого интерфейса, чтобы использовать его; существует несколько реализаций Map
, и в основном используются java.util.HashMap
и java.util.TreeMap
замечания
Карта - это объект, который хранит ключи со связанным значением для каждого ключа. Ключ и его значение иногда называют парой ключ / значение или записью . Карты обычно предоставляют следующие возможности:
- Данные сохраняются на карте в парах ключ / значение.
- Карта может содержать только одну запись для определенного ключа. Если карта содержит запись с определенным ключом, и вы пытаетесь сохранить вторую запись с тем же ключом, вторая запись заменит первую. Другими словами, это изменит значение, связанное с ключом.
- Карты обеспечивают быструю работу, чтобы проверить, существует ли ключ на карте, чтобы получить значение, связанное с ключом, и удалить пару ключ / значение.
Наиболее часто используемой реализацией карты является HashMap . Он хорошо работает с ключами, которые являются строками или цифрами.
Обычные карты, такие как HashMap, неупорядочены. Итерация через пары ключ / значение может возвращать отдельные записи в любом порядке. Если вам необходимо выполнять итерацию через записи карты контролируемым образом, вы должны посмотреть на следующее:
Сортированные карты, такие как TreeMap, будут перебирать ключи в натуральном порядке (или в порядке, который вы можете указать, предоставив Comparator ). Например, сортированная карта, использующая числа как ключи, должна была бы перебирать свои записи в числовом порядке.
LinkedHashMap позволяет выполнять итерацию через записи в том же порядке, в котором они были вставлены в карту, или по порядку последнего доступа.
Добавить элемент
- прибавление
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
System.out.println(map.get(1));
Выход: First element.
- 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 в интерфейсе карты
- Использование 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
- Использование 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
- Использование 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}
- Использование 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}
Использование 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}
Использование замены
Если ключ присутствует, значение заменяется новым значением. Если ключ отсутствует, ничего не делает.
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}
- Использование 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
- Использование 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
- Использование вычисления
Этот метод заменяет значение ключа на новое вычисляемое значение
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
- Использование слияния
Добавляет пару ключ-значение к карте, если ключ отсутствует, или значение для ключа равно 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)
, однако критерии все еще полезны для того, чтобы дать представление о том, какие реализации более эффективны в среде «реального мира».
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> pair = it.next();
sum += pair.getKey() + pair.getValue();
}
for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
sum += pair.getKey() + pair.getValue();
}
- Реализация с использованием
Map.forEach
(Java 8+)
map.forEach((k, v) -> sum[0] += k + v);
- Реализация с использованием
Map.keySet
for
for (Integer key : map.keySet()) {
sum += key + map.get(key);
}
- Реализация с использованием
Map.keySet
сIterator
Iterator<Integer> it = map.keySet().iterator();
while (it.hasNext()) {
Integer key = it.next();
sum += key + map.get(key);
}
for (Iterator<Map.Entry<Integer, Integer>> entries =
map.entrySet().iterator(); entries.hasNext(); ) {
Map.Entry<Integer, Integer> entry = entries.next();
sum += entry.getKey() + entry.getValue();
}
- Реализация с использованием
Stream.forEach
(Java 8+)
map.entrySet().stream().forEach(e -> sum += e.getKey() + e.getValue());
- Реализация с использованием
Stream.forEach
сStream.parallel
(Java 8+)
map.entrySet()
.stream()
.parallel()
.forEach(e -> sum += e.getKey() + e.getValue());
- Внедрение с использованием
IterableMap
из коллекций Apache
MapIterator<Integer, Integer> mit = iterableMap.mapIterator();
while (mit.hasNext()) {
sum += mit.next() + it.getValue();
}
- Реализация с использованием
MutableMap
из коллекций Eclipse
mutableMap.forEachKeyValue((key, value) -> {
sum += key + value;
});
Тесты производительности ( код доступен в Github )
Условия тестирования: Windows 8.1 64-бит, Intel i7-4790 3,60 ГГц, 16 ГБ
Средняя производительность 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
Средняя производительность 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
Средняя производительность 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
Сравнение вариаций производительности в зависимости от размера карты
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