java中hashmap
面试中最常见的问题是“ HashMap如何在Java中工作”,“ HashMap的获取和放置方法如何在内部工作”。 在这里,我试图通过一个简单的示例来解释内部功能。 而不是通过理论,我们将首先从示例开始,以便您可以更好地理解,然后我们将了解get和put函数如何在Java中工作。
让我们举一个非常简单的例子。 我有一个Country类,我们将使用Country类对象作为键,并使用其大写名称(字符串)作为值。 下面的示例将帮助您了解如何将这些键值对存储在哈希图中。
1. Country.java
package org.arpit.javapostsforlearning;
public class Country {String name;long population;public Country(String name, long population) {super();this.name = name;this.population = population;}public String getName() {return name;}public void setName(String name) {this.name = name;}public long getPopulation() {return population;}public void setPopulation(long population) {this.population = population;}// If length of name in country object is even then return 31(any random number) and if odd then return 95(any random number).// This is not a good practice to generate hashcode as below method but I am doing so to give better and easy understanding of hashmap.@Overridepublic int hashCode() {if(this.name.length()%2==0)return 31;else return 95;}@Overridepublic boolean equals(Object obj) {Country other = (Country) obj;if (name.equalsIgnoreCase((other.name)))return true;return false;}}
如果您想了解有关对象的哈希码和equals方法的更多信息,可以在Java中引用hashcode()和equals()方法。
2. HashMapStructure.java(主类)
import java.util.HashMap;
import java.util.Iterator;public class HashMapStructure {/*** @author Arpit Mandliya*/public static void main(String[] args) {Country india=new Country("India",1000);Country japan=new Country("Japan",10000);Country france=new Country("France",2000);Country russia=new Country("Russia",20000);HashMap<country,string> countryCapitalMap=new HashMap<country,string>();countryCapitalMap.put(india,"Delhi");countryCapitalMap.put(japan,"Tokyo");countryCapitalMap.put(france,"Paris");countryCapitalMap.put(russia,"Moscow");Iterator<country> countryCapitalIter=countryCapitalMap.keySet().iterator();//put debug point at this linewhile(countryCapitalIter.hasNext()){Country countryObj=countryCapitalIter.next();String capital=countryCapitalMap.get(countryObj);System.out.println(countryObj.getName()+"----"+capital);}}}
</country></country,string></country,string>
现在,在第23行放置调试点,然后右键单击project-> debug as-> java应用程序。 程序将在第23行停止执行,然后右键单击countryCapitalMap,然后选择watch。您将看到以下结构。
现在,从上图可以观察到以下几点
- 有一个称为table的Entry []数组,其大小为16。
- 该表存储Entry类的对象。 HashMap类具有一个称为Entry的内部类。此Entry具有键值作为实例变量。 让我们看一下入口类的结构Entry Structure。
static class Entry implements Map.Entry {final K key;V value;Entry next;final int hash;...//More code goes here }
- 每当我们尝试将任何键值对放入哈希图中时,都会为键值实例化Entry类对象,并且该对象将存储在上述Entry [](表)中。 现在您一定想知道,上面创建的Enrty对象将存储在哪里(表中的确切位置)。 答案是,通过调用Hascode()方法为密钥计算哈希码。 该哈希码用于计算上述Entry []表的索引。
- 现在,如果您在上图中的数组索引10处看到,它具有一个名为HashMap $ Entry的Entry对象。
- 我们在hashmap中放置了4个键值,但似乎只有2个!!!因为这是因为如果两个对象具有相同的哈希码,则它们将存储在相同的索引处。 现在问题出现了如何? 它以LinkedList的形式存储对象(逻辑上)。
那么如何计算上述国家/地区键值对的哈希码。
Hashcode for Japan = 95 as its length is odd.
Hashcode for India =95 as its length is odd
HashCode for Russia=31 as its length is even.
HashCode for France=31 as its length is even.
下图将清楚地说明LinkedList概念。
因此,现在,如果您对哈希图结构有很好的了解,那么让我们通过put和get方法。
放置:
让我们看一下put方法的实现:
/*** Associates the specified value with the specified key in this map. If the* map previously contained a mapping for the key, the old value is* replaced.** @param key* key with which the specified value is to be associated* @param value* value to be associated with the specified key* @return the previous value associated with <tt>key</tt>, or <tt>null</tt>* if there was no mapping for <tt>key</tt>. (A <tt>null</tt> return* can also indicate that the map previously associated* <tt>null</tt> with <tt>key</tt>.)*/public V put(K key, V value) {if (key == null)return putForNullKey(value);int hash = hash(key.hashCode());int i = indexFor(hash, table.length);for (Entry<k , V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null;}
现在让我们逐步了解上面的代码
- 检查键对象是否为空。 如果key为null,则它将存储在table [0]中,因为null的哈希码始终为0。
- 调用关键对象的hashcode()方法并计算哈希码。 该哈希码用于查找用于存储Entry对象的数组的索引。 有时可能会发生这种哈希代码函数编写不佳的情况,因此JDK设计人员放置了另一个名为hash()的函数,该函数将以上计算的哈希值作为参数。如果您想了解更多有关hash()函数的信息,可以引用hash和indexFor hashmap中的方法 。
- indexFor(hash,table.length)用于计算表数组中的确切索引,以存储Entry对象。
- 正如我们在示例中看到的那样,如果两个关键对象具有相同的哈希码(称为冲突 ),则它将以链表的形式存储。因此在这里,我们将遍历链表。
- 如果我们刚刚计算出的那个索引上没有元素,那么它将直接将Entry对象放在那个索引上。
- 如果该索引处存在元素,则它将迭代直到获得Entry-> next为null。然后当前的Entry对象成为该链表中的下一个节点
- 如果我们再次放置相同的密钥,从逻辑上讲应该替换旧值该怎么办。 是的,它将这样做。在迭代时将通过调用equals()方法( key.equals(k) )检查键是否相等,如果此方法返回true,则它将值对象替换为当前Entry的值对象。
得到:
让我们看一下get get的实现:
/*** Returns the value to which the specified key is mapped, or {@code null}* if this map contains no mapping for the key.** <p>* More formally, if this map contains a mapping from a key {@code k} to a* value {@code v} such that {@code (key==null ? k==null :* key.equals(k))}, then this method returns {@code v}; otherwise it returns* {@code null}. (There can be at most one such mapping.)** </p><p>* A return value of {@code null} does not <i>necessarily</i> indicate that* the map contains no mapping for the key; it's also possible that the map* explicitly maps the key to {@code null}. The {@link #containsKey* containsKey} operation may be used to distinguish these two cases.** @see #put(Object, Object)*/public V get(Object key) {if (key == null)return getForNullKey();int hash = hash(key.hashCode());for (Entry<k , V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k)))return e.value;}return null;}
当您了解了hashmap的put功能时。 因此,了解获取功能非常简单。 如果传递任何键以从哈希图获取值对象。
- 检查键对象是否为空。 如果key为null,则将返回Object的值位于table [0]。
- 调用关键对象的hashcode()方法并计算哈希码。
- indexFor(hash,table.length)用于使用生成的哈希码来获取Entry对象来计算表数组中的精确索引。
- 在表数组中获取索引后,它将遍历链表并通过调用equals()方法检查键是否相等,如果返回true,则返回Entry对象的值,否则返回null。
记住要点:
- HashMap有一个称为Entry的内部类,用于存储键值对。
- 上面的Entry对象存储在称为table的Entry [](Array)中
- 表的索引在逻辑上称为存储桶,它存储链表的第一个元素
- 关键对象的hashcode()用于查找该Entry对象的存储桶。
- 如果两个键对象具有相同的哈希码,则它们将进入表数组的同一存储桶中。
- 关键对象的equals()方法用于确保关键对象的唯一性。
- 完全不使用值对象的equals()和hashcode()方法
翻译自: https://www.javacodegeeks.com/2014/03/how-hashmap-works-in-java.html
java中hashmap