Що станеться, коли повторний ключ буде введено в 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, це не зазначено чітко. Я здогадуюсь, що автор документа вважав, що це мовчазне припущення для всіх реалізацій хеш-карти.
Andrew S

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

Чудова відповідь! хороша робота. Ви буквально врятуєте моє життя програмування :).
Субін Бабу

Дякую і мені! Мені довелося додати до нього метод «видалити», щоб виконати той самий функціонал, що і звичайний Map, але він працював чудово!
JGlass

1
@JGlass ваш вітаючий чувак, але це не технічне рішення, це те, що ви можете зробити, використовуючи стандартну версію Java, в технічній проблемі ви повинні стежити за своєю проблемою, якщо вам потрібно мати таку поведінку, я впевнений, що це не рішення, тому що Концепція Ключ / Значення, і потрібно продумати проблему і знайти логічний шлях до її вирішення. у будь-якому разі мої деталі - це просто цікавий спосіб зробити з Java, а у виробництві проблеми та шлях вирішення дуже відрізняються від веселої роботи! але ви могли б використовувати його, коли поведінка ключів / цінностей не є вашою проблемою та виявлення такої структури даних.
java acm

13

Пов’язує вказане значення із вказаним ключем на цій карті. Якщо раніше карта містила відображення ключа, старе значення замінюється.


12

Він замінює наявне значення на карті для відповідного ключа. І якщо жодного ключа не існує з тим самим іменем, він створює ключ із наданим значенням. наприклад:

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

OUTPUT key = "1", value = "два"

Отже, попереднє значення перезаписується.


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 ==> {one = one}

Скопійований потік:

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:284) | at AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:174) | на ReduceOps $ ReduceOp.evaluateSequences (ReduceOps.java:913) | на AbstractPipeline.evaluate (AbstractPipeline.java:234) | на ReferencePipeline.collect (ReferencePipeline.javajanju78) | в (№4: 1)

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

Існує маса інших реалізацій, які мають справу з дублюються ключами. Вони потрібні для Інтернету (наприклад, дублюються ключі файлів cookie, заголовки HTTP можуть мати однакові поля, ...)

Удачі! :)


чи дорога операція "переосмислення"?
gaurav

Це можна вирішити лише за допомогою JDK. Collectors.toMap()має третій аргумент - функція злиття. Якщо ми хочемо просто перевизначити останній дублікат елементу: Stream.of("one", "two", "one").collect(Collectors.toMap(x -> x, x -> x, (key1, key2) -> key2)). посилання
автономне

Також ваш другий приклад коду невірний. Цей вхід: "one", "not one", "surely not one"не створюватиме жодних повторюваних помилок ключа через те, що всі рядки різні.
самостійно

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

Привіт @WitoldKaczurba. Складіть свій код перед публікацією.
самостійно

1

BTW, якщо ви хочете отримати певну семантику, на зразок поставити лише якщо цього ключа не існує. ви можете використовувати 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

Означає, що хеш-карта не дозволить дублювати, якщо ви правильно перемогли рівняння та методи hashCode ().

HashSet також використовує HashMap внутрішньо, див. Вихідний документ

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