java中hashmap_HashMap如何在Java中工作

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。您将看到以下结构。

HashMapStructure1 现在,从上图可以观察到以下几点

  1. 有一个称为table的Entry []数组,其大小为16。
  2. 该表存储Entry类的对象。 HashMap类具有一个称为Entry的内部类。此Entry具有键值作为实例变量。 让我们看一下入口类的结构Entry Structure。
  3. static class Entry implements Map.Entry
    {
            final K key;
            V value;
            Entry next;
            final int hash;
            ...//More code goes here
    }
  4. 每当我们尝试将任何键值对放入哈希图中时,都会为键值实例化Entry类对象,并且该对象将存储在上述Entry [](表)中。 现在您一定想知道,上面创建的Enrty对象将存储在哪里(表中的确切位置)。 答案是,通过调用Hascode()方法为密钥计算哈希码。 该哈希码用于计算上述Entry []表的索引。
  5. 现在,如果您在上图中的数组索引10处看到,它具有一个名为HashMap $ Entry的Entry对象。
  6. 我们在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概念。

HashMapStructure2 因此,现在,如果您对哈希图结构有很好的了解,那么让我们通过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;
 }

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

  1. 检查键对象是否为空。 如果key为null,则它将存储在table [0]中,因为null的哈希码始终为0。
  2. 调用关键对象的hashcode()方法并计算哈希码。 该哈希码用于查找用于存储Entry对象的数组的索引。 有时可能会发生这种哈希代码函数编写不佳的情况,因此JDK设计人员放置了另一个名为hash()的函数,该函数将以上计算的哈希值作为参数。如果您想了解更多有关hash()函数的信息,可以引用hash和indexFor hashmap中的方法
  3. indexFor(hash,table.length)用于计算表数组中的确切索引,以存储Entry对象。
  4. 正如我们在示例中看到的那样,如果两个关键对象具有相同的哈希码(称为冲突 ),则它将以链表的形式存储。因此在这里,我们将遍历链表。
  • 如果我们刚刚计算出的那个索引上没有元素,那么它将直接将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功能时。 因此,了解获取功能非常简单。 如果传递任何键以从哈希图获取值对象。

  1. 检查键对象是否为空。 如果key为null,则将返回Object的值位于table [0]。
  2. 调用关键对象的hashcode()方法并计算哈希码。
  3. indexFor(hash,table.length)用于使用生成的哈希码来获取Entry对象来计算表数组中的精确索引。
  4. 在表数组中获取索引后,它将遍历链表并通过调用equals()方法检查键是否相等,如果返回true,则返回Entry对象的值,否则返回null。

记住要点:

  • HashMap有一个称为Entry的内部类,用于存储键值对。
  • 上面的Entry对象存储在称为table的Entry [](Array)中
  • 表的索引在逻辑上称为存储桶,它存储链表的第一个元素
  • 关键对象的hashcode()用于查找该Entry对象的存储桶。
  • 如果两个键对象具有相同的哈希码,则它们将进入表数组的同一存储桶中。
  • 关键对象的equals()方法用于确保关键对象的唯一性。
  • 完全不使用值对象的equals()和hashcode()方法

参考: HashMap如何在我们的JCG合作伙伴 Arpit Mandliya在Java框架和面向初学者博客的设计模式下在Java中工作

翻译自: https://www.javacodegeeks.com/2014/03/how-hashmap-works-in-java.html

java中hashmap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值