Putifabsent java что это
Интерфейс Map представляет отображение или иначе говоря словарь, где каждый элемент представляет пару «ключ-значение». При этом все ключи уникальные в рамках объекта Map. Такие коллекции облегчают поиск элемента, если нам известен ключ — уникальный идентификатор объекта.
Следует отметить, что в отличие от других интерфейсов, которые представляют коллекции, интерфейс Map НЕ расширяет интерфейс Collection.
Среди методов интерфейса Map можно выделить следующие:
- void clear() : очищает коллекцию
- boolean containsKey(Object k) : возвращает true, если коллекция содержит ключ k
- boolean containsValue(Object v) : возвращает true, если коллекция содержит значение v
- Set> entrySet() : возвращает набор элементов коллекции. Все элементы представляют объект Map.Entry
- boolean equals(Object obj) : возвращает true, если коллекция идентична коллекции, передаваемой через параметр obj
- boolean isEmpty : возвращает true, если коллекция пуста
- V get(Object k) : возвращает значение объекта, ключ которого равен k. Если такого элемента не окажется, то возвращается значение null
- V getOrDefault(Object k, V defaultValue) : возвращает значение объекта, ключ которого равен k. Если такого элемента не окажется, то возвращается значение defaultVlue
- V put(K k, V v) : помещает в коллекцию новый объект с ключом k и значением v. Если в коллекции уже есть объект с подобным ключом, то он перезаписывается. После добавления возвращает предыдущее значение для ключа k, если он уже был в коллекции. Если же ключа еще не было в коллекции, то возвращается значение null
- V putIfAbsent(K k, V v) : помещает в коллекцию новый объект с ключом k и значением v, если в коллекции еще нет элемента с подобным ключом.
- Set keySet() : возвращает набор всех ключей отображения
- Collection values() : возвращает набор всех значений отображения
- void putAll(Map map) : добавляет в коллекцию все объекты из отображения map
- V remove(Object k) : удаляет объект с ключом k
- int size() : возвращает количество элементов коллекции
Чтобы положить объект в коллекцию, используется метод put , а чтобы получить по ключу — метод get . Реализация интерфейса Map также позволяет получить наборы как ключей, так и значений. А метод entrySet() возвращает набор всех элементов в виде объектов Map.Entry .
Обобщенный интерфейс Map.Entry представляет объект с ключом типа K и значением типа V и определяет следующие методы:
- boolean equals(Object obj) : возвращает true, если объект obj, представляющий интерфейс Map.Entry , идентичен текущему
- K getKey() : возвращает ключ объекта отображения
- V getValue() : возвращает значение объекта отображения
- V setValue(V v) : устанавливает для текущего объекта значение v
- int hashCode() : возвращает хеш-код данного объекта
При переборе объектов отображения мы будем оперировать этими методами для работы с ключами и значениями объектов.
Классы отображений. HashMap
Базовым классом для всех отображений является абстрактный класс AbstractMap , который реализует большую часть методов интерфейса Map. Наиболее распространенным классом отображений является HashMap , который реализует интерфейс Map и наследуется от класса AbstractMap.
Пример использования класса:
import java.util.*; public class Program < public static void main(String[] args) < Mapstates = new HashMap(); states.put(1, "Germany"); states.put(2, "Spain"); states.put(4, "France"); states.put(3, "Italy"); // получим объект по ключу 2 String first = states.get(2); System.out.println(first); // получим весь набор ключей Set keys = states.keySet(); // получить набор всех значений Collection values = states.values(); //заменить элемент states.replace(1, "Poland"); // удаление элемента по ключу 2 states.remove(2); // перебор элементов for(Map.Entry item : states.entrySet()) < System.out.printf("Key: %d Value: %s \n", item.getKey(), item.getValue()); >Map people = new HashMap(); people.put("1240i54", new Person("Tom")); people.put("1564i55", new Person("Bill")); people.put("4540i56", new Person("Nick")); for(Map.Entry item : people.entrySet()) < System.out.printf("Key: %s Value: %s \n", item.getKey(), item.getValue().getName()); >> > class Person < private String name; public Person(String value)< name=value; >String getName() >
Чтобы добавить или заменить элемент, используется метод put, либо replace, а чтобы получить его значение по ключу — метод get. С помощью других методов интерфейса Map также производятся другие манипуляции над элементами: перебор, получение ключей, значений, удаление.
Использование computeIfAbsent из Map
Методу computeIfAbsent передаётся два параметра: ключ key и функция расчета значения по этому ключу mappingFunction .
Логика работы метода:
- Проверяем наличие такого ключа в map. Если ключ есть и значение по ключу не равно null , то ничего не делаем
- Иначе (нет ключа или значение по ключу равно null ) считаем значение, применяя mappingFunction к key
- Если итоговое значение не равно null , то записываем пару ключ-значение в map
В виде кода эта логика в документации описывается так:
if (map.get(key) == null)
В некоторых ситуациях достаточно будет метода putIfAbsent, особенно если вычисляемое значение вообще не зависит от ключа.
Например, если в Map> map нужно просто положить новый список по ключу:
map.putIfAbsent(key, new ArrayList<>());
Вот если при этом хочется сразу и значение положить в этот список, то тут удобнее будет computeIfAbsent :
map.computeIfAbsent(key, k -> new ArrayList<>()).add(100);
map.putIfAbsent(key, new ArrayList<>()); map.get(key).add(100);
Хорошим примером использования метода computeIfAbsent с запоминанием результата является вычисление чисел Фибоначчи:
private static Map map = new HashMap<>(); static < map.put(0, 0L); map.put(1, 1L); >public static long fibonacci(int x) < return map.computeIfAbsent(x, n ->fibonacci(n - 2) + fibonacci(n - 1)); >
System.out.println(fibonacci(10));
В чем разница между putIfAbsent и computeIfAbsent в Java 8 Map?
Обе функции стремятся добавить элемент, если указанный ключ еще не присутствует в карте. putIfAbsent добавляет элемент с указанным значением, в то время как computeIfAbsent добавляет элемент со значением, вычисленным с помощью ключа. http://www.buggybread.com/2014/10/java-8-difference-between-map.html
Мы видели, что putIfAbsent удаляет обязательный способ определения if-statement, но что, если получение статей в Java действительно негативно сказывается на производительности? Для оптимизации этого мы не хотим получать статьи, пока не будем уверены, что они нам действительно нужны — это означает, что нам нужно знать, отсутствует ли ключ перед получением статей. http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute-merge-and-replace/
Я не был готов понять, в чем различия, пожалуйста, можете подробнее рассказать об этих двух функциях? java java-8
Поделиться Источник 10 января 2018 в 09:21
7 ответов
Разница #1 computeIfAbsent принимает функцию отображения, которая вызывается для получения значения, если ключ отсутствует. putIfAbsent принимает значение напрямую. Если значение дорогое для получения, то putIfAbsent теряет его, если ключ уже существует. Общее «дорогое» значение, например, new ArrayList<>() для создания Map> , где создание нового списка, когда ключ уже существует (который затем отбрасывает новый список), генерирует ненужный мусор. Разница #2 computeIfAbsent возвращает «текущее (существующее или вычисленное) значение, связанное с указанным ключом, или null, если вычисленное значение равно null». putIfAbsent возвращает «предыдущее значение, связанное с указанным ключом, или null, если не было отображения для ключа». Таким образом, если ключ уже существует, они возвращают то же самое, но если ключ отсутствует, computeIfAbsent возвращает вычисленное значение, в то время как putIfAbsent возвращает null. Разница #3 Оба метода определяют «absent», так как отсутствующий или существующее значение равно null, но: computeIfAbsent не будет добавлять значение null, если ключ отсутствует. putIfAbsent будет добавлять значение, если ключ отсутствует, даже если значение равно null. Это не имеет значения для будущих вызовов computeIfAbsent , putIfAbsent и вызовов get , но это имеет значение для вызовов, таких как getOrDefault и containsKey .
Поделиться 10 января 2018 в 09:31
Предположим, у вас есть Map
map.putIfAbsent("key", new ValueClass());
в любом случае создаст экземпляр ValueClass , даже если ключ «key» уже есть в Map .Это просто создаст ненужный экземпляр. С другой стороны
map.computeIfAbsent("key", k -> new ValueClass());
создаст экземпляр ValueClass только в том случае, если ключ «ключ» еще не находится в Map (или сопоставлен со значением null ). Поэтому computeIfAbsent более эффективен. putIfAbsent эквивалентен:
ValueClass value = new ValueClass(); if (map.get("key") == null)
в то время как computeIfAbsent эквивалентен:
if (map.get("key") == null)
Еще небольшое отличие между двумя методами заключается в том, что computeIfAbsent не будет добавлять значение null для отсутствующего ключа. putIfAbsent будет.
Поделиться 10 января 2018 в 09:26
- putIfAbsent принимает ключ и значение и помещает значение в карту, если для этого ключа нет значения на карте.
- computeIfAbsent принимает ключ и Function . Если для этого ключа нет значения на карте, функция вызывается для создания значения, которое затем помещается в карту.
Если у вас уже есть значение, используйте putIfAbsent .
Если у вас еще нет значения, и создание значения является дорогой операцией (например, значение должно быть найдено в базе данных), то используйте computeIfAbsent , чтобы дорогая операция не была выполнена в том случае, если карта уже содержит значение для указанного ключа.
Поделиться 10 января 2018 в 09:29
Возможно, реализация по умолчанию может немного уточнить.
default V putIfAbsent(K key, V value) Реализация по умолчанию эквивалентна, для этой карты:
V v = map.get(key); if (v == null) v = map.put(key, value); return v;
С другой стороны:
default V computeIfAbsent(K key, Function mappingFunction)
if (map.get(key) == null)
Поделиться 10 января 2018 в 09:38
V putIfAbsent(K key, V value) — Если указанный ключ еще не связан со значением (или маппирован на null), попытается вычислить его значение с помощью указанной функции отображения и вводит его в эту карту, если только не будет пустое значение.
V computeIfAbsent(K key, Function mappingFunction) — Если указанный ключ еще не связан со значением (или маппирован на null), попытается вычислить его значение с помощью указанной функции отображения и вводит его в эту карту, если только не будет пустое значение.
Поделиться 10 января 2018 в 09:37
Этот вопрос уже был отвечен. Мне понадобилось немного времени, чтобы понять («дорогостоящая операция не должна выполняться в случае, если карта уже содержит значение для указанного ключа в computeIfAbsent») Я помещаю свое понимание здесь. Надеюсь, это будет полезно для других:
putIfAbsent() ведет себя просто как put() , когда карта уже содержит значение для указанного ключа. putIfAbsent() проверяет только то, является ли ключ пустым или нет. Если он не пустым, то он возвращает значение, а затем снова получает это значение.
Однако в computeIfAbsent() есть проверка на пустое значение как для ключа, так и для значения. При проверке на пустое значение, если оно не пустое, то существующее значение из объекта карты присваивается newValue и возвращается. Поэтому нет необходимости получать значение снова, так как существующее значение из карты повторно используется.
Ссылка на следующую программу для справки:
public class MapTest1 < public static final String AJAY_DEVGAN = "Ajay Devgn"; public static final String AUTOBIOGRAPHY = "Autobiography"; public static void main(String[] args) < MapTest1 mt = new MapTest1(); mt.testPutCompute(); >private void testPutCompute() < Map> movies = getMovieObject(); System.out.println("\nCalling putIfAbsent method. "); //System.out.println(movies.get(AJAY_DEVGAN)); //movies.put(AJAY_DEVGAN, getAjayDevgnMovies()); movies.putIfAbsent(AJAY_DEVGAN, getAjayDevgnMovies()); System.out.println("\nCalling computeIfAbsent method. "); //System.out.println(movies.get(AUTOBIOGRAPHY)); movies.computeIfAbsent(AUTOBIOGRAPHY, t -> getAutobiographyMovies()); > private Map> getMovieObject() < Map> movies = new HashMap<>(); movies.put(AJAY_DEVGAN, getAjayDevgnMovies()); movies.put(AUTOBIOGRAPHY, getAutobiographyMovies()); System.out.println(movies); return movies; > private List getAutobiographyMovies() < System.out.println("Getting autobiography movies"); Listlist = new ArrayList<>(); list.add("M.S. Dhoni - The Untold Story"); list.add("Sachin: A Billion Dreams"); return list; > private List getAjayDevgnMovies() < System.out.println("Getting Ajay Devgn Movies"); ListajayDevgnMovies = new ArrayList<>(); ajayDevgnMovies.add("Jigar"); ajayDevgnMovies.add("Pyar To Hona Hi Tha"); return ajayDevgnMovies; > >
Ссылка на следующий код для putIfAbsent() и computeIfAbsent() из интерфейса Map.class
public interface Map < . default V putIfAbsent(K key, V value) < V v = get(key); if (v == null) < v = put(key, value); >return v; > default V computeIfAbsent(K key, Function mappingFunction) < Objects.requireNonNull(mappingFunction); V v; if ((v = get(key)) == null) < V newValue; if ((newValue = mappingFunction.apply(key)) != null) < put(key, newValue); return newValue; >> return v; > . >
Поделиться 25 сентября 2019 в 13:05
Возьмите этот простой пример для putIfAbsent() :
Map myMap = new HashMap(); myMap.put(1,"ABC"); myMap.put(2,"XYZ"); myMap.put(3,"GHI"); //Output of map till this point is : 1-> "ABC", 2-> "XYZ", 3-> "GHI" myMap.putIfAbsent(3,"cx"); //Output of map till this point is : 1-> "ABC", 2-> "XYZ", 3-> "GHI"
cx будет значением 3 , если в карте уже нет значения 3 .
Удобные методы работы с Map
В Java 8 базовый интерфейс Map расширился новыми полезными методами. На конкретных примерах я покажу, как приходилось работать с этим типом коллекций раньше, и как легко с ним стало работать теперь.
Обход всей коллекции в цикле
Для того, чтобы пройтись по всем элементам коллекции и вывести значения на экран, раньше мы бы писали такой код:
for (Map.Entry
>
В java 8 появился метод foreach(), который принимает лямбда-функцию из двух параметров: ключ и значение. Указанный выше код можно записать одной строкой:
map.forEach((k, v) -> System.out.println(v));
Значение по умолчанию
Если в объекте типа Map нет значения по указанному ключу, то мы получим null. Если потом с объектом предполагается выполнить какие-то действия, то мы рискуем получить NullPointerException. Чтобы обезопасить себя от исключения, раньше мы бы писали так:
SomeClass c = map.get( «key» );
if (c == null ) c = new SomeClass();
>
c.someMethod();
Теперь у нас есть метод getOrDefault(), позволяющий задать значение по умолчанию:
map.getOrDefault( «key» , new SomeClass()).someMethod();
Вставка элемента с проверкой
Используя метод put(), мы можем добавлять новые элементы. Если же по данному ключу элемент уже был, то повторный вызов заменит это значение. Если же мы хотим предварительно проверить, нет ли уже такого элемента, то раньше мы бы писали такой код:
if (!map.containsKey( «key» )) <
map.put( «key» , «value» );
>
Теперь есть специальный метод putIfAbsent(), делающий ровно то же самое:
map.putIfAbsent( «key» , «value» );
Предварительная инициализация значения
Часто в качестве значения Map лежит какой-нибудь список. И чтобы добавить в этот список новый элемент по указанному ключу, нам нужно предварительно убедиться, что список инициализирован. Раньше мы бы писали так:
if (!map.containsKey( «key» )) <
map.put( «key» , new ArrayList<>());
>
map.get( «key» ).add( «value» );
Теперь используем лямбда-функцию в методе computeIfAbsent(), которая вызывается только в том случае, если ключа ещё нет в нашей мапе:
map.computeIfAbsent( «key» , k -> new ArrayList<>()).add( «value» );
Слияние значений
Бывает и такое, что в Map в качестве значений лежат некие числовые показатели, например, счётчики. И при возникновении нового значения, мы не должны потерять старое значение, если оно есть. Мы должны просуммировать его с новым:
Integer oldValue = map.get( «key» );
if (oldValue == null ) map.put( «key» , newValue);
> else map.put( «key» , oldValue + newValue);
>
Благодаря методу merge() весь указанный выше код может быть переписан с использованием лямбды:
map.merge( «key» , newValue, (x, y) -> x + y);
Выводы
Как видим, в Java 8 появился ряд новых удобных методов по работе с Map, которые позволяют выполнять рутинные операции буквально в одну строчку, используя лямбда-выражения.