HashTable 如何在Java内部工作?
Hashtable 是一种 Hash 映射,但是是同步的。哈希映射是非同步的,允许一个空键和多个空值,不是线程安全的,即没有适当的同步就不能在多个线程之间共享,键/值对存储在哈希表中。哈希表不允许空值/键。使用它时,您指定一个用作键的对象以及要与键关联的值。它是一个索引数组。每个索引被称为一个桶/槽。它根据key和通过调用Hash code()方法识别出的bucket的位置来约束值。 Java的哈希表包含一个具有唯一元素的类。
哈希表的工作
哈希表本质上包含一个槽/桶,其中存储键值对。它使用键的哈希码来发现集合的键/值应该映射到哪个桶。要在列表中查找项目,您可以使用第一种方法,即线性搜索,这涉及检查每个项目,这将花费更多时间。假设您检索了一个数组的值及其索引号,那么您将很快查找一个值。事实上,如果您知道索引号与数组的大小和位置无关,那么时间会更少。但是你怎么知道数组的哪个元素包含你正在寻找的值。答案是通过计算值本身,索引号与数据有些相关。
让我们增殖数组,取一个名字并找到它的 ASCII 码,例如,我们将取一个名字 say Mia 为每个字母找到 ASCII 码并添加 ASCII 码并将组合的 ASCII 数字除以其中一个元素的大小是 5,我们将提醒元素为 4,然后将名称 Mia ie 放在第 4 个插槽/桶中。
例子:
Name = Mia
M 77
i 105
a 97
Sum = 279
Modulus = 279 % 5 = 4
现在我们可以向 Bucket 添加更多名称,如下所示:
Mia ⇾ M (77) + i(105) + a(97) = 279 % 5 = 4
Tim ⇾ T (84) + i(105) + m(77) = 298 % 5 = 3
Leo ⇾ L(76) + e(101) + n(110) = 287 % 5 = 2
Som ⇾ S(83) + o(111) + m(77) = 271 % 5 = 1
Beb ⇾ B(66) + e(101) + b(98) = 265 % 5 0
Index number = sum of ASCII codes % Size of array
让我们检索一个项目,说 Ada 我们执行相同的计算,
Find ASCII for Ada = (65+100+97) = 262
262 % 11 = 9
MyData = Array (9)
这是一个快速的数组查找。而不是存储单个项目或数据哈希表用于存储键值对。例如,Ada 将是要为其计算相应值的 Index 和 DOB 的键,因此,如果采用面向对象的方法,键值对的哈希表有时也被称为哈希映射将每个人放在一个类的实例中,并且键将具有许多属性,通过使用对象填充数组,您将有效地为每个存储桶存储您喜欢的相关数据
散列算法也称为散列函数。哈希函数是在给定键时生成表的函数。这是一个从 Key 的哈希码中获取桶位置的函数。它每次返回一个对象的数字。计算应用于密钥以将其转换为地址。
对于数字键,将键除以可用地址的数量 n,然后取余数
Address = key Mod n
对于字母数字密钥,将密钥中 ASCII 代码的总和除以可用地址的数量,并取余数。
- 折叠方法将钥匙分成相等的部分然后将这些部分加在一起
- 电话018767242947变成01+87+67+24+29+47=255
- 根据表的大小,然后可以除以某个常数并取余数。
- 根据性质,有许多哈希函数适合另一种。
哈希表中的冲突
到目前为止,您已经看到,有很多带有数据的哈希表非常方便地导致任何问题,有时如果您在两种不同的情况下应用哈希函数,它会为两者生成相同的索引号,但两个项目都可以不要去同一个地方,这就是所谓的碰撞。两个相同的对象每次都具有相同的数字,而两个不相等的对象可能并不总是具有不同的数字。将一个对象放入哈希表,不同的对象可能具有相同的哈希代码,称为碰撞。为了纠正这个列表数组,哈希表使用。映射到数组索引/单个存储桶并保存在索引中的集合,索引引用存储在数组索引中。
这次让我们用一组不同的数据再次加载数组。如下所示,如果 Mia 占据位置 4,Leo 占据位置 2,并且如果其他名字在位置 2 上也有相同的条目,那么下一个存储桶将自动填充该条目以避免碰撞,如下所示。
Find Rae 280 Mod 11 = 5
myData = Array(5)
由于 Rae 的 ASCII 码的提醒是 5 但被放在第 8 位,因为由于碰撞,这 2 个项目计算出在同一个插槽中,但为了避免这种情况,Rae 被转移到另一个插槽,另一个插槽又是被占用,所以它被转移到第 8 位。
通过将项目放在其他地方然后其计算出的地址来解决冲突称为 OPEN ADDRESSING,因为每个位置都对项目开放。这可以使用多种技术来决定放置项目的位置,如果计算出的地址被占用,则这种特定寻址技术称为线性探测,如果线性探测结束并且仍然存在,则使用线性搜索来查找下一个可用桶找不到它可能会围绕数组开头循环的位置并从那里继续搜索。哈希表中的项目越多,发生冲突的机会就越大。解决此问题的一种方法是针对您期望的数据总量使哈希表更大,这样可能会发生 70% 的表被占用,存储的项目数与数组大小之间的比率称为负载因素
Load Factor = Total number of items stored / Size of the array
另一种处理冲突的方法称为链,有时也称为 CLOSED ADDRESSING。在这里,一个插槽可以通过将各种项目添加到一个桶中来形成一个链。
哈希函数的目标
- 尽量减少碰撞
- 哈希值的均匀分布
- 易于计算
- 解决任何冲突
像 hashCode() & equal() 之类的方法来查找精确的元素匹配
equal():对象类将 equal() 定义为Java的一种给定语言。其中对象指示是否有少数对象作为“等于”当前实例的参数传递。只有将不同的或两个对象放在相同的内存地址上时,它们才能相似。
句法:
public boolean equals (Object obj)
// This method checks if some other Object
// passed to it as an argument is equal to
// the Object on which it is invoked.
应遵循以下原则:
- 自反:对于单个对象说a , a.equal(a) 返回 true。
- 对称:对于两个不同的对象,当且仅当 y.equal(a) 返回 true 时,a.equal(y) 返回 true。
- 传递性:对于多个对象 a, b, c 如果 a.equal(b) 返回 true & b.equals(c) 返回 true 那么 a.equals(c) 应该返回 true。
- 如果对象属性被修改,则在 equal() 方法实现中使用的方法将导致不同,并且 a.equal(b) 的倍数将返回相等的结果。
- 只有当两个引用标记相似的对象时,对象类 equal() 才会返回 true。
注意:对于任何非空引用值 a,a.equals(null) 应返回 false。
hashCode():在这个对象中,返回一个对象内存地址的整数描述。通过使用此方法,将返回一个随机整数,该整数对于特定实例是唯一的。这将返回该对象的整数哈希码数据或值。
语法:
public int hashCode()
// This method returns the hash code value
// for the object on which this method is invoked.
例子:
Java
import java.util.*;
public class HashCodeExample {
public static void main(String[] args)
{
// develop integer object
Integer i = new Integer("144");
// Returned hash code value
int hashValue = i.hashCode();
System.out.println("Value of Hashcode : "
+ hashValue);
}
}
Java
import java.util.*;
public class HashtableExample {
public static void main(String[] args)
{
// Create Hashtable
Hashtable hashtable
= new Hashtable<>();
// Add mappings to hashtable
hashtable.put(1, "X");
hashtable.put(2, "Y");
hashtable.put(3, "Z");
System.out.println(hashtable);
// Get a mapping by key
String value = hashtable.get(1);
System.out.println(value);
// Remove a mapping
hashtable.remove(3);
// Iterate overmapping
Iterator itr
= hashtable.keySet().iterator();
while(itr.hasNext())
{
Integer key = itr.next();
String mapped_value = hashtable.get(key);
System.out.println(
"key: " + key + " value : " + mapped_value);
}
}
}
Java
import java.util.Hashtable;
import java.util.Enumeration;
public class HashtableExample {
public static void main(String[] args)
{
Enumeration names;
String key;
// Developing a Hashtable
Hashtable hashtable
= new Hashtable();
// Attaching Key and Value sets to Hashtable
hashtable.put("Key1", "Madhu");
hashtable.put("Key2", "Girja");
hashtable.put("Key3", "Durgesh");
hashtable.put("Key4", "Richa");
hashtable.put("Key5", "Manisha");
names = hashtable.keys();
while (names.hasMoreElements()) {
key = (String)names.nextElement();
System.out.println("Key: " + key + " & Value: "
+ hashtable.get(key));
}
}
}
输出
144
脚步
- 基类具有用户定义的对象属性。
- 通过使用@override 可以预定义覆盖 hashCode()函数。
- 以类似的方式,可以预定义覆盖 equal()函数。
- 声明基类的一个对象并使用哈希码和 equals函数,以便对象在主函数中相等。
hash code()和equal()的契约
- 在应用程序的执行过程中,如果对同一个对象多次调用 hashCode() ,那么它必须始终返回相同的整数值,前提是没有修改对象上的equals(Object)比较中使用的信息。从应用程序的一次执行到同一应用程序的另一次执行,此整数值不必保持相同。
- 如果两个对象相等,根据equals(Object)方法,那么 hashCode() 方法必须在两个对象中的每一个上生成相同的 Integer。
- 如果两个Object不相等,根据equals(Object)方法,hashCode()方法在两个Object上产生的Integer值不一定是不同的。它可以是相同的,但在两个对象中的每一个上生成不同的整数对于提高基于散列的集合(如 HashMap、HashTable 等)的性能更好。
注意:相等的对象只要相等就必须产生相同的哈希码,但不相等的对象不需要产生不同的哈希码。
用途
它是一种执行关联数组超越数据类型的数据结构,一种可以将键映射到值的结构。这里我们使用哈希算法将一个称为哈希码的列表确定为一个槽数组,从中可以获得所需的值。哈希表用于在诸如Python、 PHP等编程语言中实现内存表,用于错误检查。关联数组、数据库索引、缓存、集合、对象表示、唯一数据表示、转置表等。
示例 1:
Java
import java.util.*;
public class HashtableExample {
public static void main(String[] args)
{
// Create Hashtable
Hashtable hashtable
= new Hashtable<>();
// Add mappings to hashtable
hashtable.put(1, "X");
hashtable.put(2, "Y");
hashtable.put(3, "Z");
System.out.println(hashtable);
// Get a mapping by key
String value = hashtable.get(1);
System.out.println(value);
// Remove a mapping
hashtable.remove(3);
// Iterate overmapping
Iterator itr
= hashtable.keySet().iterator();
while(itr.hasNext())
{
Integer key = itr.next();
String mapped_value = hashtable.get(key);
System.out.println(
"key: " + key + " value : " + mapped_value);
}
}
}
{3=Z, 2=Y, 1=X}
X
key: 2 value : Y
key: 1 value : X
示例 2:
Java
import java.util.Hashtable;
import java.util.Enumeration;
public class HashtableExample {
public static void main(String[] args)
{
Enumeration names;
String key;
// Developing a Hashtable
Hashtable hashtable
= new Hashtable();
// Attaching Key and Value sets to Hashtable
hashtable.put("Key1", "Madhu");
hashtable.put("Key2", "Girja");
hashtable.put("Key3", "Durgesh");
hashtable.put("Key4", "Richa");
hashtable.put("Key5", "Manisha");
names = hashtable.keys();
while (names.hasMoreElements()) {
key = (String)names.nextElement();
System.out.println("Key: " + key + " & Value: "
+ hashtable.get(key));
}
}
}
Key: Key4 & Value: Richa
Key: Key3 & Value: Durgesh
Key: Key2 & Value: Girja
Key: Key1 & Value: Madhu
Key: Key5 & Value: Manisha
结论
- 用于索引大量数据
- 使用密钥本身计算的每个密钥的地址
- 使用开放式或封闭式寻址解决冲突
- 散列广泛用于数据库索引、编译器、缓存、密码认证等
- 插入、删除和检索在恒定时间内发生。