get
في put
HashMap؟ ". سأحاول هنا شرح الوظيفة الداخلية باستخدام مثال بسيط. بدون الخوض في الكثير من النظريات، سنبدأ بمثال حتى تتمكن من فهم أفضل ومن ثم معرفة كيفية عمل الأساليب في Java get
أيضًا . put
دعونا نأخذ مثالا بسيطا جدا. لدينا فئة Country
("بلد" باللغة الإنجليزية)، وسوف نستخدم كائن الفئة Country
كمفتاح، واسم عاصمة هذا البلد كقيمة. فيما يلي مثال لمساعدتنا في فهم كيفية تخزين زوج المفتاح والقيمة في خريطة التجزئة.
1. البلد.جافا
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)
). إذا كانت النتيجة صحيحة، فسيتم استبدال القيمة القديمة بقيمة الكائن الحالي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;
}
الآن بعد أن فهمت كيفية عمل طريقة الوضع في خرائط التجزئة، أصبح فهم كيفية عمل طريقة الوضع أمرًا
get
بسيطًا للغاية. عند تمرير أي مفتاح إلى طريقة للحصول على قيمة من خريطة التجزئة:
-
شيء
يتم اختبار Ekey من أجل المساواة null
. إذا كان الأمر كذلك، فسيتم إرجاع قيمة الكائن المخزن في الخليةtable[0]
؛ -
يحتوي الكائن الرئيسي على طريقة تسمى
hashcode()
تحسب رمز التجزئة؛ -
indexFor(hash,table.length)
تستخدم لتحديد خلية صفيف محددةtable
يمكن أخذ كائن فئة منهاEntry
؛ -
بعد تلقي رقم خلية المصفوفة،
table
سيتم تكرار القائمة ومقارنة المفاتيح باستخدام الطريقةequals()
. إذا كانت النتيجة صحيحة، فسيتم إرجاع قيمة الكائنEntry
، وإلا -null
.
أشياء للذكرى:
-
تحتوي الفئة
HashMap
على فئة داخليةEntry
تخزن أزواج القيمة الرئيسية؛ -
يتم تخزين كائنات الفئة
Entry
في مصفوفةEntry[ ]
تسمىtable
; -
تسمى خلية المصفوفة بالدلو وتقوم بتخزين العنصر الأول من القائمة المرتبطة؛
-
يتم استخدام طريقة
hashcode()
الكائنkey
للعثور على مجموعة كائنات الفئة هذهEntry
؛ -
إذا كان لمفتاحي كائنين نفس رمز التجزئة، فسيتم تخزينهما في نفس مجموعة المصفوفة
table
؛ -
يتم استخدام طريقة
equals()
الكائنkey
لتأكيد تفرده؛ -
لا يتم استخدام الأساليب
equals()
والكائنات على الإطلاق.hashcode()
value
GO TO FULL VERSION