Java Hashmap教程

原文: https://javabeginnerstutorial.com/core-java-tutorial/java-hashmap/

Java HashMap是基于哈希表的Map实现。 这就是为什么面试官总是要求HashMapHashTable之间存在差异的原因。 HashMap基本上等于HashTable,但以下两个区别除外。

  1. HashTable同步时,HashMap不同步。
  2. HashMap允许为null,而HashTable不允许为null

HashMap的重要属性

DEFAULT_INITIAL_CAPACITY默认初始容量(2 的 n 次幂)。 HashMap可以包含许多元素。
MAXIMUM_CAPACITYHashMap的最大容量(2 的 n 次幂)。
loadFactor定义HashMap的阈值。 重新调整大小时将在HashMap中发生。
DEFAULT_LOAD_FACTORHashMap的构造器中未定义任何负载因子时将使用。
sizeHashMap包含的键/值对映射数。

创建HashMap

在创建HashMap时未定义任何参数时,将使用默认的初始容量(16)和默认的负载系数(0.75)。 该HashMap最多可以包含 16 个元素,并且在插入第 13 个元素时会调整HashMap的大小。 这是因为负载系数为 75% (.75),并且在添加第 13 个元素(12 + 1)时将超过此阈值。

您还可以提供初始容量和loadFactor。 但是初始容量不能超过最大容量(2 的 30 次幂),并且负载系数不能为零或负数。

HashMap中元素的添加

为了添加一个元素,您需要提供 2 个东西:键和值。

:将与指定值关联的键。 null是允许的。

:与指定键关联的值。

首先,HashMap将为给定键生成一个哈希码,然后检查是否已经有与给定键相关联的值。 如果是,则它将返回已经关联的值。 否则,它将通过提供的键在HashMap中增加值。

重点

  1. HashMap不会按Map中的元素顺序提供任何保证(均值顺序会随时间变化)。
  2. HashMap获得设置操作提供恒定时间性能(如果使用正确的哈希算法)。
  3. 迭代集合所需的时间与HashMap的“容量”(它可以容纳的元素)和大小(它当前容纳的元素)成比例。
  4. 如果迭代性能更为重要,那么建议不要将初始容量设置得太高而将负载系数设置得太低。 由于性能与初始容量负载系数成正比。
    • 容量是哈希表中的存储桶数。
    • 初始容量(默认值为 16)只是创​​建哈希表时的容量。
    • *负载因子(默认值 .75)*是衡量哈希表在自动增加其容量之前的填充程度的度量。
    • 当哈希表中的条目数超过负载因子与当前容量的乘积时,哈希表将被重映射(即内部数据结构将被重建)。
  5. 使用“Collections.synchronizedMap()”方法使映射同步。
  6. HashMap类返回的迭代器为“故障快速”。
  7. HashMap由数组(Key)和LinkedListValue)支持。
  8. HashMap使用hashcode(使用键)来标识应在HashMap中放置或检索对象的确切位置。
  9. 最后,HashCode返回后备数组中的确切位置(索引)。
  10. 支持数组的大小固定。 因此,只要数组已满(此映射中的键数达到其阈值)。 将创建一个具有新容量的新数组,并将所有元素添加到该新数组中。
  11. 在两种情况下(添加和检索对象)都将使用HashCode,而在任何情况下均可以使用或可以不使用equals()方法。
  12. HashMapKey的最佳候选者是具有正确实现EqualsHashcode方法的不可变类(示例:字符串类)。
  13. 更好的哈希码和equals方法实现是HashMap的更好性能。
  14. 这样,所有String和原始类型的包装类将是HashMap中键的理想选择。

什么是重新哈希

每个HashMap都有预定义的大小(初始容量),以及在需要时(超过阈值限制时)增加此大小(负载系数)的逻辑。

示例

使用以下配置创建HashMap

初始容量:16(默认初始容量

负载系数:.75(默认负载系数

在给定的HashMap中添加第 13 个元素后,超过给定HashMap的阈值限制,系统将创建一个新的后备键集数组(此数组的大小将是前一个数组的两倍)。 系统将不得不再次计算确切的存储桶,应放置上一个存储桶中的元素,并将旧HashMap中的所有元素复制到新的HashMap中。 整个过程称为重新哈希,因为会再次为每个元素计算Hashcode

因为超时的HashMap可能会被重新散布并且顺序可能会发生变化。

HashMap的示例

在此示例中,您将学到以下几点

  1. 如何迭代映射
  2. 迭代映射的不同方法
  3. HashCodeEquals何时被调用。 (请特别注意HashCode的输出和equals方法调用)
  1. import java.util.HashMap;
  2. import java.util.Iterator;
  3. import java.util.Map;
  4. import java.util.Set;
  5. public class HashMapExample {
  6. public static void main(String[] args) {
  7. Map<JBT, Integer> m1 = new HashMap<JBT, Integer>();
  8. JBT t1 = new JBT(1, 2);
  9. JBT t2 = new JBT(1, 3);
  10. JBT t3 = new JBT(2, 1);
  11. m1.put(t1, 1);
  12. m1.put(t2, 2);
  13. m1.put(t3, 3);
  14. System.out.println("Addition Done");
  15. /*
  16. * Below you can find 3 different ways to iterate a Map. Uncomment
  17. * different section and see the different in Output. Pay attention to
  18. * when Hashcode and Equals is called
  19. */
  20. /* Set s = m1.entrySet();
  21. for (Iterator i = s.iterator(); i.hasNext();) {
  22. Map.Entry me = (Map.Entry) i.next();
  23. System.out.println(me.getKey() + " : " + me.getValue());
  24. }
  25. */
  26. /* for (Map.Entry<JBT, Integer> entry : m1.entrySet()) {
  27. System.out.println("Key : " + entry.getKey() + " Value : "
  28. + entry.getValue());
  29. }
  30. */
  31. for (Object key : m1.keySet()) {
  32. System.out.println("Key : " + key.toString() + " Value : "
  33. + m1.get(key));
  34. }
  35. }
  36. }
  37. class JBT {
  38. JBT(int i, int j) {
  39. this.i = i;
  40. this.j = j;
  41. }
  42. int i, j;
  43. @Override
  44. public int hashCode() {
  45. System.out.println("Inside HashCode Method");
  46. int k = i + j;
  47. return k;
  48. }
  49. @Override
  50. public boolean equals(Object obj) {
  51. System.out.println("Inside Equals Method");
  52. if (i == ((JBT) obj).i && j == ((JBT) obj).j)
  53. return true;
  54. else
  55. return false;
  56. }
  57. @Override
  58. public String toString() {
  59. return String.valueOf(i).concat(String.valueOf(j));
  60. }
  61. }