HashMap如何在Java中工作

时间:2020-02-23 14:34:14  来源:igfitidea点击:

HashMap内部的get和put方法是如何工作的?
其中我正在尝试用一个简单的例子来解释内部功能。
HashMap是Java中最常用的集合之一。

我们将首先从示例开始,以便我们将获得更好的理解,然后我们将看到java中的功能和put()函数工作。

让我们来一个非常简单的例子。
我有一个国家级,我们将使用国家类对象作为键及其首批名称(字符串)作为值。
下面的示例将理解,这些密钥值对将存储在HashMap中。

  1. Country.java
package org.igi.theitroad;
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;
	}
 
}

如果我们想了解更多关于HashCode和Equals对象方法的信息,则可以在Java中引用HashCode()和equals()方法

2. HashMapStructure.java(主类)

import java.util.HashMap;
import java.util.Iterator;
 
public class HashMapStructure {
 
	/**
	 * @author igi Mandliya
	 */
	public static void main(String[] args) {
 
		Country Netherlands=new Country("Netherlands",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(Netherlands,"Delhi");  
		countryCapitalMap.put(japan,"Tokyo");  
		countryCapitalMap.put(france,"Paris");  
		countryCapitalMap.put(russia,"Moscow");  
 
		Iterator 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);  
		}  
	}  
 
 
}

现在将调试点放在第24行,并右键单击"项目 - >调试" - > Java应用程序。
程序将在第24行停止执行,然后右键单击CountryCapitalMap,然后选择Watch.You将能够看到如下结构。

现在从上图中,我们可以观察以下几点

  • 有一个名为tabes的条目[]数组,其具有16尺寸。
  • 此表存储了条目类对象。 hashmap类有一个名为条目的内部类。此条目具有键值作为实例变量。让我们看看输入类条目结构的结构。
static class Entry implements Map.Entry
{
        final K key;
        V value;
        Entry next;
        final int hash;
        ...//More code goes here
}
  • 每当我们尝试在HashMap中置于HashMap中的任何键值对时,将实例化为键值的条目类对象,并且该对象将存储在上面提到的条目中。现在我们必须想知道,在创建的条目对象上面会存储(表中的确切位置)。答案是,通过调用hascode()方法来计算哈希码。此哈希码用于计算上面条目[]表的索引。
  • 现在,如果我们在上图中看到数组索引10,它有一个名为hashmap $条目的条目对象。
  • 我们在hashmap中放了4个键值,但它似乎只有2 !!!!这是因为如果两个对象具有相同的哈希码,它们将存储在相同的索引上。

现在出现了问题如何?它以LinkedList(逻辑)的形式存储对象。

所以计算上述国家钥匙值对的哈希码。

Hashcode for Japan = 95 as its length is odd.
Hashcode for Netherlands =95 as its length is odd
HashCode for Russia=31 as its length is even.
HashCode for France=31 as its length is even.

现在,如果我们对HashMap结构的良好理解,请允许通过放置并获得方法。

put

让我们看看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 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;
		}

现在让我们一步一步地了解上面的代码

  • 检查键对象是否为NULL。如果键是NULL,则它将存储在表[0]中,因为空隙为null始终为0。
  • 调用key对象的HashCode()方法,并计算哈希码。

此哈希码用于查找用于存储条目对象的数组索引。有时它可能会发生这种情况,这个散码函数写得很差,所以JDK设计器已经汇出了另一个名为HASH()的函数,它将上面计算的散列值作为参数。如果要了解有关Hash()函数的更多信息,可以在HashMap中引用Hash和IndexFor方法。

  • indexfor(hash,table.length)用于计算用于存储条目对象的表数组中的精确索引。
  • 正如我们在我们的示例中所见,如果两个关键对象具有相同的哈希码(称为碰撞),那么它将以LinkedList.so的形式存储,我们将通过我们的LinkedList迭代。
  • 如果在该索引上没有提供的元素,我们刚刚计算出该索引,那么它将直接将我们的入口对象放在该索引上。
  • 如果该索引存在于该索引上的元素,则它将迭代,直到它获得rest-> next作为null。
  • 如果我们再次放置相同的键,逻辑上会替换旧值。是的,它会这样做。虽然迭代它将通过调用Equals()方法检查关键平等(key.equals(k)),如果此方法返回true,则替换具有当前条目的值对象的值对象。
  • 如果没有找到重复的键,则当前条目对象将成为LinkedList中的第一个节点,并且当前条目 - >下一个将成为该索引上的现有第一个节点。

get

让我们看看现在的实现:

/**
	 * Returns the value to which the specified key is mapped, or {@code null}
	 * if this map contains no mapping for the key.
	 *
	 *
	 * 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.)
	 *
	 *
	 * A return value of {@code null} does not necessarily 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 Jan 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 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获取值对象。

  • 检查键对象是否为NULL。如果键是NULL,则对象的值驻留在表[0]中返回。
  • 调用key对象的HashCode()方法,并计算哈希码。
  • indexfor(hash,table.length)用于使用生成的哈希码来计算表阵列中的精确索引来获取条目对象。
  • 在表阵列中获取索引后,它将通过LinkedList迭代并通过调用Equals()方法来检查关键平等,如果返回true,则返回extring对象的值返回null。

需要记住的要点

  • HashMap有一个名为条目的内部类,存储键值对。
  • 上面的条目对象存储在名为table的条目
  • 表索引是逻辑称为桶,它存储LinkedList的第一个元素。
  • Key对象的HashCode()用于查找该输入对象的桶。
  • 如果两个关键对象具有相同的哈希码,则它们将在相同的表阵列中进行。
  • 关键对象的equals()方法用于确保关键对象的唯一性。
  • 值对象的等于()和hashcode()方法根本不使用