Что происходит, когда дубликат ключа помещается в HashMap?


276

Если я прохожу те же клавишу несколько раз HashMap«S putметодом, что происходит с первоначальным значением? А что если даже значение повторяется? Я не нашел никакой документации по этому вопросу.

Случай 1: перезаписанные значения для ключа

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
System.out.println(mymap.get("1"));

Мы получаем surely not one.

Случай 2: Дублирующее значение

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
// The following line was added:
mymap.put("1","one");
System.out.println(mymap.get("1"));

Мы получаем one.

Но что происходит с другими ценностями? Я преподавал основы студенту, и меня спросили об этом. Это Mapкак корзина, где указано последнее значение (но в памяти)?


7
Кстати, это отличная возможность продемонстрировать мультихеш-карту, которая является частью классов коллекций Джакарты ( commons.apache.org/collections ). Это позволит вам иметь любое количество значений, связанных с одним и тем же ключом, для тех случаев, когда вам это нужно.
Джон Мунш

Ответы:


303

По определению putкоманда заменяет предыдущее значение, связанное с данным ключом на карте (концептуально, как операция индексации массива для примитивных типов).

Карта просто сбрасывает ссылку на значение. Если ничто иное не содержит ссылку на объект, этот объект становится пригодным для сборки мусора. Кроме того, Java возвращает любое предыдущее значение, связанное с данным ключом (или, nullесли его нет), так что вы можете определить, что там было, и при необходимости сохранить ссылку.

Больше информации здесь: HashMap Doc


Спасибо за это. При чтении документации Java это не упоминается четко. Я предполагаю, что автор документа предположил, что это молчаливое предположение о всех реализациях хэш-карт.
Андрей С

77

Вы можете найти свой ответ в javadoc Map # put (K, V) (который на самом деле что-то возвращает):

public V put(K key,
             V value)

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

Параметры:
key - ключ, с которым должно быть связано указанное значение.
value- значение, которое будет связано с указанным ключом.

Возвращает:
предыдущее значение, связанное с указанным ключом, или nullесли не было сопоставления для key. ( nullВозврат также может указывать, что карта, ранее связанная nullс указанным key, если реализация поддерживает nullзначения.)

Таким образом, если вы не присваиваете возвращаемое значение при вызове mymap.put("1", "a string"), оно просто перестает ссылаться и, следовательно, может быть использовано для сборки мусора.


3
Возвращаемое значение является предыдущее значение (или null) как описано чуть выше в Javadoc так, да, это то , что я имею в виду. Это действительно может быть неправильно истолковано?
Паскаль Thivent

это очень полезно
roottraveller

18

Предыдущее значение для ключа сбрасывается и заменяется новым.

Если вы хотите сохранить все значения, заданные ключом, вы можете рассмотреть возможность реализации чего-то вроде этого:

import org.apache.commons.collections.MultiHashMap;
import java.util.Set;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
public class MultiMapExample {

   public static void main(String[] args) {
      MultiHashMap mp=new MultiHashMap();
      mp.put("a", 10);
      mp.put("a", 11);
      mp.put("a", 12);
      mp.put("b", 13);
      mp.put("c", 14);
      mp.put("e", 15);
      List list = null;

      Set set = mp.entrySet();
      Iterator i = set.iterator();
      while(i.hasNext()) {
         Map.Entry me = (Map.Entry)i.next();
         list=(List)mp.get(me.getKey());

         for(int j=0;j<list.size();j++)
         {
          System.out.println(me.getKey()+": value :"+list.get(j));
         }
      }
   }
}

1
Это решение ограничено. MultiHashMap является частью apache.commons.collections, а не Java.
wikimix

17

это функция «ключ / значение», и вы не можете иметь дубликат ключа для нескольких значений, потому что, когда вы хотите получить фактическое значение, одно из значений которого принадлежит введенному ключу
в вашем примере, когда вы хотите получить значение «1», какое из них является Это ?!
это причина иметь уникальный ключ для каждого значения, но вы могли бы использовать трюк с помощью стандартной библиотеки Java:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class DuplicateMap<K, V> {

    private Map<K, ArrayList<V>> m = new HashMap<>();

    public void put(K k, V v) {
        if (m.containsKey(k)) {
            m.get(k).add(v);
        } else {
            ArrayList<V> arr = new ArrayList<>();
            arr.add(v);
            m.put(k, arr);
        }
    }

     public ArrayList<V> get(K k) {
        return m.get(k);
    }

    public V get(K k, int index) {
        return m.get(k).size()-1 < index ? null : m.get(k).get(index);
    }
}


и вы могли бы использовать это таким образом:

    public static void main(String[] args) {
    DuplicateMap<String,String> dm=new DuplicateMap<>();
    dm.put("1", "one");
    dm.put("1", "not one");
    dm.put("1", "surely not one");
    System.out.println(dm.get("1"));
    System.out.println(dm.get("1",1));
    System.out.println(dm.get("1", 5));
}

и результат отпечатков:

[one, not one, surely not one]
not one
null

Отличный ответ! хорошая работа. Вы буквально спасаете мое программирование life :).
Субин Бабу

Спасибо и мне тоже! Мне пришлось добавить метод «remove», чтобы он выполнял те же функции, что и обычная карта, но работал отлично!
JGlass

1
@JGlass приветственный чувак, но это не техническое решение, это то, что вы можете сделать с помощью стандартной библиотеки Java, в техническом плане вы должны следить за своей проблемой, если вам нужно такое поведение, я уверен, что это не решение, потому что концепции ключ / значение, и нужно думать о проблеме и найти логический способ ее решения. В любом случае, мои данные - это просто забавный способ работы с Java и в процессе производства, проблемы и пути решения очень отличаются от веселой работы! но вы можете использовать его, когда поведение «ключ / значение» не является вашей проблемой, и вы обнаружите, что у вас есть такая структура данных.
Ява ACM

13

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


12

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

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","two");

ВЫВОД Ключ = "1", значение = "два"

Итак, предыдущее значение перезаписывается.


4

На ваш вопрос, была ли карта похожа на ведро: нет.

Это как список с name=valueпарами, тогда какname как не обязательно быть строкой (хотя может).

Чтобы получить элемент, вы передаете свой ключ в метод get (), который возвращает вам назначенный объект.

И хэш карта означает , что если вы пытаетесь восстановить свой объект , используя GET-метод, он не будет сравнивать реальный объект , который вы при условии, потому что нужно будет перебирать свой список и сравнить () ключ Вы предоставили текущий элемент.

Это было бы неэффективно. Вместо этого, независимо от того, из чего состоит ваш объект, он вычисляет так называемый хэш-код из обоих объектов и сравнивает их. Проще сравнить два ints вместо двух целых (возможно, очень сложных) объектов. Вы можете представить себе хеш-код в виде сводки, имеющей предопределенную длину (int), поэтому он не уникален и имеет коллизии. Правила для хэш-кода вы найдете в документации, на которую я вставил ссылку.

Если вы хотите узнать больше об этом, вы можете взглянуть на статьи на javapractices.com и technofundo.com

С уважением


3

Я всегда использовал:

HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();

если бы я хотел применить несколько вещей к одному идентифицирующему ключу.

public void MultiHash(){
    HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();
    String key = "Your key";

    ArrayList<String> yourarraylist = hashy.get(key);

    for(String valuessaved2key : yourarraylist){
        System.out.println(valuessaved2key);
    }

}

Вы всегда можете сделать что-то подобное и создать себе лабиринт!

public void LOOK_AT_ALL_THESE_HASHMAPS(){
    HashMap<String, HashMap<String, HashMap<String, HashMap<String, String>>>> theultimatehashmap = new HashMap <String, HashMap<String, HashMap<String, HashMap<String, String>>>>();
    String ballsdeep_into_the_hashmap = theultimatehashmap.get("firststring").get("secondstring").get("thirdstring").get("forthstring");
}

2

Карты из JDK не предназначены для хранения данных под дублированными ключами.

  • В лучшем случае новое значение переопределит предыдущие.

  • Хуже сценарий является исключением (например, когда вы пытаетесь собрать его в виде потока):

Нет дубликатов:

Stream.of("one").collect(Collectors.toMap(x -> x, x -> x))

Хорошо. Вы получите: $ 2 ==> {один = один}

Дублированный поток:

Stream.of("one", "not one", "surely not one").collect(Collectors.toMap(x -> 1, x -> x))

Исключение java.lang.IllegalStateException: дубликат ключа 1 (попытка объединения значений один, а не один) | в Collectors.duplicateKeyException (Collectors.java:133) | в Collectors.lambda $ uniqKeysMapAccumulator $ 1 (Collectors.java:180) | в ReduceOps $ 3ReducingSink.accept (ReduceOps.java:169) | в Spliterators $ ArraySpliterator.forEachRemaining (Spliterators.java:948) | at AbstractPipeline.copyInto (AbstractPipeline.java:484) | at AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:474) | at ReduceOps $ ReduceOp.evaluateSequential (ReduceOps.java:913) | at AbstractPipeline.evaluate (AbstractPipeline.java:234) | в ReferencePipeline.collect (ReferencePipeline.java:578) | в (# 4: 1)

Для работы с дублированными ключами - используйте другой пакет, например: https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html.

Существует много других реализаций, имеющих дело с дублированными ключами. Они необходимы для Интернета (например, дублированные ключи cookie, заголовки Http могут иметь те же поля, ...)

Удачи! :)


дорогостоящая операция переопределения?
Гаурав

Это можно решить, используя только JDK. Collectors.toMap()имеет третий аргумент - функцию слияния. Если мы хотим просто переопределить последний дубликат элемента: Stream.of("one", "two", "one").collect(Collectors.toMap(x -> x, x -> x, (key1, key2) -> key2)). ссылка
отдельно

Также ваш второй пример кода не верен. Этот ввод: "one", "not one", "surely not one"не будет выдавать дубликатов ошибок ключа, потому что все строки разные.
одинокий

Привет @ стой один. Пожалуйста, внимательно прочитайте функцию отображения (toMap).
Витольд Качурба

Привет @WitoldKaczurba. Пожалуйста, скомпилируйте свой код перед публикацией.
одиноко

1

Кстати, если вы хотите некоторую семантику, например, поставить, если этот ключ не существует. Вы можете использовать concurrentHashMapс putIfAbsent()функцией. Проверь это:

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html#put(K,%20V)

concurrentHashMap Потокобезопасен с высокой производительностью, поскольку использует механизм « чередования блокировок » для повышения пропускной способности.


1

Да, это означает, что все 1 ключи со значением перезаписываются последним добавленным значением, и здесь вы добавляете «наверняка не один», поэтому он будет отображать только «наверняка не один».

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


0
         HashMap<Emp, Emp> empHashMap = new HashMap<Emp, Emp>();

         empHashMap.put(new Emp(1), new Emp(1));
         empHashMap.put(new Emp(1), new Emp(1));
         empHashMap.put(new Emp(1), new Emp());
         empHashMap.put(new Emp(1), new Emp());
         System.out.println(empHashMap.size());
    }
}

class Emp{
    public Emp(){   
    }
    public Emp(int id){
        this.id = id;
    }
    public int id;
    @Override
    public boolean equals(Object obj) {
        return this.id == ((Emp)obj).id;
    }

    @Override
    public int hashCode() {
        return id;
    }
}


OUTPUT : is 1

Означает, что хеш-карта не допускает дублирования, если вы правильно переопределили методы equals и hashCode ().

HashSet также использует HashMap для внутреннего использования, см. Исходный документ

public class HashSet{
public HashSet() {
        map = new HashMap<>();
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.