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.
@Override
public int hashCode() {
if(this.name.length()%2==0)
return 31;
else
return 95;
}
@Override
public 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 line
while(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