get
”,“HashMap中方法工作的内部机制是什么put
?”。在这里我将尝试使用一个简单的示例来解释内部功能。在不涉及太多理论的情况下,我们将从一个示例开始,以便您可以更好地理解并了解方法在 Java 中的工作get
原理。put
让我们举一个非常简单的例子。我们有一个类Country
(英语“国家”),我们将使用类对象Country
作为键,并使用该国家的首都名称作为值。下面的例子可以帮助我们理解键值对如何存储在哈希图中。
1. 国家.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;
}
// если длина имени в an objectе Country - четное число,
// то возвращаем 31(любое случайное число), а если нечетное - 95 (любое случайное число).
// указанный ниже метод - это не самый лучший способ генерации хеш-codeа,
// но мы воспользуемся им для более четкого понимания хеш-карт.
@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
和等于的更多信息,可以点击此链接。
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();//установите
//debug-точку на этой строке(23)
while(countryCapitalIter.hasNext())
{
Country countryObj=countryCapitalIter.next();
String capital=countryCapitalMap.get(countryObj);
System.out.println(countryObj.getName()+"----"+capital);
}
}
}
现在将断点设置在第23行并运行run -> debug as -> java application(译者注——对Eclipse有效)。程序将在第 23 行停止执行,之后右键单击CountryCapitalMap并选择watch。您将看到这样的表格: 这里我们看到以下内容:
-
有一个包含
Entry[]
16 个单元格的数组,名为table
; -
该数组存储该类的对象
Entry
。该类HashMap
有一个内部类 -Entry
。该类的实例是键值对。让我们看一下类结构Entry
: -
每次我们尝试在哈希映射中创建键值对时,都会为该对创建一个类对象
Entry
,并将其存储在上表中Entry[]
。现在您应该想知道该对象将准确写入该表中的哪个位置(在哪个单元格中)。对于键值对中的键,使用 计算哈希码hashcode()
。这个哈希码用于计算表单元格数量Entry[]
; -
现在,如果您查看表的单元格 10,您将看到一个
Entry
名为 的类对象HashMap$Entry
; - 我们添加了 4 个键值对,但数组中只有 2 个!!!这是因为如果两个对象具有相同的哈希码,那么它们将存储在同一个单元格中。但如何呢?对象将被存储为链表(
LinkedList
)。
static class Entry implements Map.Entry
{
final K key;
V value;
Entry next;
final int hash;
...//продолжение codeа
}
Hashcode for Japan = 95 так How длина слова Japan имеет нечетное количество букв.
Hashcode for India = 95 так How длина слова India имеет нечетное количество букв.
HashCode for Russia = 31 так How длина слова Russia имеет четное количество букв.
HashCode for France = 31 так How длина слова France имеет четное количество букв.
下图将解释链表的思想: 现在您已经了解了哈希映射的结构,让我们继续讨论put
和方法get
。
放:
我们来看看这个方法是如何使用的:/**
* Метод связывает указанное meaning с указанным ключом в данной хэш-карте. Если
* карта до этого уже содержала некоторое meaning, соответствующее этому ключу,
* то старое meaning заменяется на указанное.
* @param key
* ключ, с которым связывается указанное meaning
* @param value
* meaning, связываемое с указанным ключом
* @возвращает meaning связанное с <tt>ключом</tt>, or <tt>null</tt>,
* если ниHowое meaning не соответствует <tt>ключу</tt>. ( Возврат <tt>null</tt>
* может так же говорить о том, что в карте заведомо <tt>null</tt> был связан с
* <tt>ключом</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
。如果是,则该对象key
将存储在该位置table[0]
,因为 的哈希码null
始终为 0; -
接下来,我们调用该对象的
key
方法hashcode()
,该方法将计算其哈希码。该散列码用于确定将存储类对象的数组单元Entry
。有时碰巧这个函数hashcode
写得不太熟练,于是JDK开发者创建了一个不同的函数——hash()
,它以之前计算的哈希码作为参数。如果您有兴趣更详细地了解此功能,可以点击链接; -
indexFor(hash,table.length)
用于定义数组中的特定单元格,table
其中将定义要存储的类对象Entry
; -
正如我们在示例中看到的,如果两个对象
key
具有相同的哈希码(这种情况称为冲突),那么它们将以链表的形式存储。因此,在这个阶段我们迭代我们的列表: -
如果新计算的单元格为空,则类对象
Entry
将直接保存到该单元格中; -
next
如果此单元格已包含某个对象,则迭代到字段等于 的元素null
。之后,我们的类对象Entry
成为列表中的下一个; -
如果我们再次添加同一个对象怎么办
key
?从逻辑上讲,它应该替换旧值。是的,会这样。equals()
在迭代过程中,将使用( ) 方法对键进行比较key.equals(k)
。如果结果为 true,则旧值将被当前对象的值替换Entry
。
得到:
现在我们来看看该方法的应用/**
* returns meaning, которое соответствует указанному ключу, or {@code null}, если
* данная карта не содержит пары с указанным ключом.
*
*
* <p>
* Более точно, если в данной карте содержится такой ключ {@code k}
* с соответствующим ему meaningм {@code v}, что {@code (key==null ? k==null : key.equals(k))},
* то метод возвращает {@code v}; в противном случае возвращается {@code null}.
* (может быть не более одной такой пары)
*
* </p><p>
* Возвращенное meaning {@code null} не <i>обязательно</i> говорит о том, что
* в карте нет пары с таким указанным ключом; а возможно, что в карте однозначно
* указано соответствие этого ключа со meaningм {@code null}.
* Можно воспользоваться операцией {@link #containsKey containsKey}, чтобы
* отличить эти два случая
* @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;
}
现在您已经了解了 put 方法在哈希图中的工作原理,理解 put 方法的工作原理就
get
非常简单了。当您将任何键传递给方法以从哈希映射中获取值时:
-
一个东西
Ekey 被测试是否相等 null
。如果是,则返回存储在单元格中的对象的值table[0]
; -
密钥对象有一个称为
hashcode()
计算哈希码的方法; -
indexFor(hash,table.length)
table
用于确定从中获取类对象的特定数组单元Entry
; -
收到数组单元格编号后,
table
它将迭代列表并使用方法比较键equals()
。如果结果为 true,则返回该对象的值Entry
,否则返回 -null
。
要记住的事情:
-
该类
HashMap
有一个Entry
存储键值对的内部类; -
该类的对象
Entry
存储在一个Entry[ ]
名为 的数组中table
; -
数组单元称为桶,存储链表的第一个元素;
-
hashcode()
object方法key
用于查找该类对象的桶Entry
; -
如果两个对象的key具有相同的哈希码,则它们将被存储在同一个数组桶中
table
; -
equals()
对象的方法key
用于确认其唯一性; -
根本不使用方法
equals()
和hashcode()
对象。value
GO TO FULL VERSION