Java中的HashMap

Java中的HashMap

在Java中,HashMapJava 1.2版本后的Java集合的一部分。这个类位于 java.util 包中。它提供了Java Map接口的基本实现。Java中的HashMap将数据存储在(键,值)对中,您可以通过另一种类型(如Integer)的索引访问它们。一个对象用作键(索引)到另一个对象(值)的键。如果您尝试在HashMap中插入重复的键,则会替换相应键的元素。

HashMap是什么?

Java HashMap 与HashTable相似,但它是非同步的。它允许存储null键,但只能有一个null键对象,并且可以有任意数量的null值。这个类不保证映射的顺序。要使用这个类及其方法,您需要导入 java.util.HashMap 包或它的超类。

Java HashMap示例

// Java program to illustrate HashMap class of java.util
// package
  
// Importing HashMap class
import java.util.HashMap;
  
// Main class
public class GFG {
  
    // Main driver method
    public static void main(String[] args)
    {
        // Create an empty hash map by declaring object
        // of string and integer type
        HashMap<String, Integer> map = new HashMap<>();
  
        // Adding elements to the Map
        // using standard put() method
        map.put("vishal", 10);
        map.put("sachin", 30);
        map.put("vaibhav", 20);
  
        // Print size and content of the Map
        System.out.println("Size of map is:- "
                           + map.size());
  
        // Printing elements in object of Map
        System.out.println(map);
  
        // Checking if a key is present and if
        // present, print value by passing
        // random element
        if (map.containsKey("vishal")) {
  
            // Mapping
            Integer a = map.get("vishal");
  
            // Printing value for the corresponding key
            System.out.println("value for key"
                               + " \"vishal\" is:- " + a);
        }
    }
}

输出

Size of map is:- 3
{vaibhav=20, vishal=10, sachin=30}
value for key "vishal" is:- 10

HashMap声明

public class HashMap<K,V> extends AbstractMap<K,V>
                         implements Map<K,V>,Cloneable,Serializable

参数:

它有两个参数,分别是:

  • 该映射维护的键的类型
  • 映射值的类型

Java中的HashMap实现了 SerializableCloneable ,Map<;K,V>;接口。Java HashMap扩展了 AbstractMap <K,V> ** 类,它的直接子类有LinkedHashMap和 **PrinterStateReasons

Java HashMap在Java中的层次结构

Java中的HashMap

HashMap的特点:

HashMap是一种数据结构,用于基于键存储和检索值。HashMap的一些关键特征包括:

  • 访问速度快 : HashMap 可以提供常数时间访问元素,这意味着检索和插入元素非常快,通常是 O(1)的时间复杂度。
  • 使用哈希函数 : HashMap 使用哈希函数将键映射到数组中的索引。这允许根据键快速查找值。
  • 存储键值对 : HashMap 中的每个元素都由一个键值对组成。键用于查找关联值。
  • 支持 null 键和值 : HashMap 允许使用 null 值和键。这意味着可以使用 null 键来存储值,并且可以将 null 值与键关联起来。
  • 无序 : HashMap 无序,这意味着不保留向 map 添加元素的顺序。但是,LinkedHashMap 是 HashMap 的一个变种,它保留插入顺序。
  • 允许重复 : HashMap 允许重复值,但不允许重复键。如果添加重复键,则将覆盖与该键关联的前一个值。
  • 线程不安全 : HashMap 不是线程安全的。这意味着如果多个线程同时访问同一个 hashmap,可能会导致数据不一致。如果需要线程安全性,可以使用 ConcurrentHashMap。
  • 容量和负载因子 : HashMap 有一个容量,即它可以容纳的元素数,以及一个负载因子,即在调整大小之前 hashmap 可以容纳的最大数量。

在Java中创建 HashMap:

import java.util.HashMap;
  
public class ExampleHashMap {
   public static void main(String[] args) {
        
      // 创建 HashMap
      HashMap<String, Integer> hashMap = new HashMap<>();
        
      // 向 HashMap 中添加元素
      hashMap.put("John", 25);
      hashMap.put("Jane", 30);
      hashMap.put("Jim", 35);
        
      // 访问 HashMap 中的元素
      System.out.println(hashMap.get("John")); // 输出:25
        
      // 从 HashMap 中删除元素
      hashMap.remove("Jim");
        
      // 检查 HashMap 中是否存在元素
      System.out.println(hashMap.containsKey("Jim")); // 输出:false
        
      // 获取 HashMap 的大小
      System.out.println(hashMap.size()); // 输出:2
   }
}

输出结果

25
false
2

HashMap 中的构造函数

HashMap 提供 4 个构造函数,每个构造函数的访问修饰符均为 public,它们如下所示:

  1. HashMap()
  2. HashMap(int initialCapacity)
  3. HashMap(int initialCapacity, float loadFactor)
  4. HashMap(Map map)

接下来,一边逐个讨论上述构造函数,一边借助简洁的 Java 程序实现。

1. HashMap()

它是默认构造函数,创建一个 HashMap 的实例,初始容量为 16,负载因子为 0.75。

语法:

HashMap<K, V> hm = new HashMap<K, V>();

示例:

// Java程序演示HashMap()构造函数
  
// 导入基本所需类
import java.io.*;
import java.util.*;
  
// 主类
// 向HashMap中添加元素
class GFG {
  
    // 主驱动程序方法
    public static void main(String args[])
    {
        // 不需要两次提及泛型类型
        HashMap<Integer, String> hm1 = new HashMap<>();
  
        // 使用泛型初始化HashMap
        HashMap<Integer, String> hm2
            = new HashMap<Integer, String>();
  
        // 使用put方法添加元素
        // 自定义输入元素
        hm1.put(1, "one");
        hm1.put(2, "two");
        hm1.put(3, "three");
  
        hm2.put(4, "four");
        hm2.put(5, "five");
        hm2.put(6, "six");
  
        // 打印并显示HashMap 1的映射
        System.out.println("HashMap hm1的映射是: "
                           + hm1);
  
        // 打印并显示HashMap 2的映射
        System.out.println("HashMap hm2的映射是: "
                           + hm2);
    }
}

输出

HashMap hm1的映射是: {1=one, 2=two, 3=three}
HashMap hm2的映射是: {4=four, 5=five, 6=six}

2. HashMap(int initialCapacity)

它创建了一个HashMap实例,具有指定的初始容量和负载因子为0.75。

语法:

HashMap<K, V> hm = new HashMap<K, V>(int initialCapacity);

示例:

// Java程序演示HashMap(int initialCapacity)构造函数
  
// 导入基本类
import java.io.*;
import java.util.*;
  
// 主类
// 向HashMap中添加元素
class AddElementsToHashMap {
  
    // 主驱动程序方法
    public static void main(String args[])
    {
        // 不需要两次提及泛型类型
        HashMap<Integer, String> hm1 = new HashMap<>(10);
  
        // 使用泛型初始化HashMap
        HashMap<Integer, String> hm2
            = new HashMap<Integer, String>(2);
  
        // 使用put方法向HashMap对象添加元素
  
        // HashMap 1
        hm1.put(1, "one");
        hm1.put(2, "two");
        hm1.put(3, "three");
  
        // HashMap 2
        hm2.put(4, "four");
        hm2.put(5, "five");
        hm2.put(6, "six");
  
        // 打印HashMap 1的元素
        System.out.println("HashMap hm1的映射是: "
                           + hm1);
  
        // 打印HashMap 2的元素
        System.out.println("HashMap hm2的映射是: "
                           + hm2);
    }
}

输出

HashMap hm1的映射是: {1=one, 2=two, 3=three}
HashMap hm2的映射是: {4=four, 5=five, 6=six}

3. HashMap(int initialCapacity, float loadFactor)

它创建了一个HashMap实例,具有指定的初始容量和指定的负载因子。

语法:

HashMap<K, V> hm = new HashMap<K, V>(int initialCapacity, float loadFactor);

示例:

// Java程序演示HashMap(int initialCapacity, float loadFactor)构造函数
  
// 导入基本类
import java.io.*;
import java.util.*;
  
// 主类
// 向HashMap中添加元素
class AddElementsToHashMap {
  
    // 主驱动程序方法
    public static void main(String args[])
    {
        // 不需要两次提及泛型类型
        HashMap<Integer, String> hm1 = new HashMap<>(10, 0.75f);
  
        // 使用泛型初始化HashMap
        HashMap<Integer, String> hm2
            = new HashMap<Integer, String>(2, 0.5f);
  
        // 使用put方法添加元素到HashMap中
  
        // HashMap 1
        hm1.put(1, "one");
        hm1.put(2, "two");
        hm1.put(3, "three");
  
        // HashMap 2
        hm2.put(4, "four");
        hm2.put(5, "five");
        hm2.put(6, "six");
  
        // 打印HashMap 1的元素
        System.out.println("HashMap hm1的映射是: "
                           + hm1);
  
        // 打印HashMap 2的元素
        System.out.println("HashMap hm2的映射是: "
                           + hm2);
    }
}

输出

HashMap hm1的映射是: {1=one, 2=two, 3=three}
HashMap hm2的映射是: {4=four, 5=five, 6=six}
// Java program to Demonstrate
// HashMap(int initialCapacity,float loadFactor) Constructor

// 导入基本类
import java.io.*;
import java.util.*;

// 主类
// 向HashMap添加元素
class GFG {

    // 主驱动程序方法
    public static void main(String args[])
    {
        // 不需要两次声明泛型类型
        HashMap<Integer, String> hm1
            = new HashMap<>(5, 0.75f);

        // 使用泛型初始化HashMap
        HashMap<Integer, String> hm2
            = new HashMap<Integer, String>(3, 0.5f);

        // 使用put()方法添加元素
        // 自定义输入元素
        hm1.put(1, "one");
        hm1.put(2, "two");
        hm1.put(3, "three");

        hm2.put(4, "four");
        hm2.put(5, "five");
        hm2.put(6, "six");

        // 在对象hashMap 1中打印和显示元素
        System.out.println("Mappings of HashMap hm1 are : "
                                   + hm1);

        // 在对象hashMap 2中打印和显示元素
        System.out.println("Mapping of HashMap hm2 are : "
                                   + hm2);
    }
}

输出结果

Mappings of HashMap hm1 are : {1=one, 2=two, 3=three}
Mapping of HashMap hm2 are : {4=four, 5=five, 6=six}

4. HashMap(Map map)

它创建一个与指定映射相同映射的HashMap实例。

HashMap<K, V> hm = new HashMap<K, V>(Map map);

// Java program to demonstrate the 
// HashMap(Map map) Constructor

import java.io.*;
import java.util.*;

class AddElementsToHashMap {
    public static void main(String args[])
    {
        // 不需要两次声明泛型类型
        Map<Integer, String> hm1 = new HashMap<>();

        // 使用put方法添加元素
        hm1.put(1, "one");
        hm1.put(2, "two");
        hm1.put(3, "three");

        // 使用泛型初始化HashMap
        HashMap<Integer, String> hm2
            = new HashMap<Integer, String>(hm1);

        System.out.println("Mappings of HashMap hm1 are : "
                                   + hm1);

        System.out.println("Mapping of HashMap hm2 are : "
                                   + hm2);
    }
}

输出结果

Mappings of HashMap hm1 are : {1=one, 2=two, 3=three}
Mapping of HashMap hm2 are : {1=one, 2=two, 3=three}

在HashMap上执行各种操作

1.添加元素

为了向map中添加元素,我们可以使用put()方法。 然而,Hashmap中不保留插入顺序。 在内部,对于每个元素,会生成一个单独的哈希值,并根据此哈希将元素进行索引,使其更高效。

// Java program to traverse
// elements of HashMap
  
import java.io.*;
import java.util.*;
  
class TraversalOfHashMap {
    public static void main(String[] args)
    {
        // Creation of a HashMap
        HashMap<String, Integer> hm
            = new HashMap<String, Integer>();
  
        // Adding elements to the HashMap
        hm.put("a", 1);
        hm.put("b", 2);
        hm.put("c", 3);
        hm.put("d", 4);
  
        // Traversal of HashMap
        Iterator<Map.Entry< String,
                         Integer>> iterator
            = hm.entrySet().iterator();
  
        while (iterator.hasNext()) {
            Map.Entry< String, Integer> entry
                = (Map.Entry< String, Integer>)iterator.next();
  
            System.out.println("Key = " + entry.getKey()
                               + ", Value = " + entry.getValue());
        }
    }
}

输出

Key = a, Value = 1
Key = b, Value = 2
Key = c, Value = 3
Key = d, Value = 4
// Java程序遍历一个
// Java.util.HashMap
  
import java.util.HashMap;
import java.util.Map;
  
public class 遍历HashMap {
    public static void main(String[] args)
    {
        // 初始化HashMap
        HashMap<String, Integer> map = new HashMap<>();
  
        // 使用put方法添加元素
        map.put("vishal", 10);
        map.put("sachin", 30);
        map.put("vaibhav", 20);
  
        // 使用for-each循环迭代map
        for (Map.Entry<String, Integer> e : map.entrySet())
            System.out.println("Key: " + e.getKey()
                               + " Value: " + e.getValue());
    }
}

输出结果

Key: vaibhav Value: 20
Key: vishal Value: 10
Key: sachin Value: 30

HashMap的重要特点

要访问值,必须知道其键。HashMap被称为HashMap,因为它使用一种称为散列的技术。Hashing是一种将大型字符串转换为表示相同字符串的小字符串的技术。较短的值有助于索引和更快的搜索。HashSet也在内部使用HashMap。
HashMap的一些重要特点是:

  • HashMap是java.util包的一部分。
  • HashMap扩展了抽象类AbstractMap,它还提供了Map接口的不完整实现。
  • 它还实现了Cloneable和Serializable接口。上述定义中的K和V分别表示键和值。
  • HashMap不允许重复的键,但允许重复的值。这意味着单个键不能包含多个值,但多个键可以包含单个值。
  • HashMap也允许空键,但只有一次和多个空值。
  • 此类不保证映射的顺序;特别是,它不保证顺序会随时间保持不变。它与HashTable大致相似,但不是同步的。

HashMap的内部结构

在内部,HashMap包含一个Node数组,而节点表示为包含4个字段的类:

  1. int hash
  2. K key
  3. V value
  4. Node next

可以看出,节点包含对自己对象的引用。所以它是一个链接列表。

HashMap:

Java中的HashMap

节点:

Java中的HashMap

HashMap的性能

HashMap的性能取决于两个参数,分别是:

  1. 初始容量
  2. 负载因子

1. 初始容量 —— HashMap在创建时的容量(HashMap实例化时可以容纳的桶数)。在Java中,初始值为 2^4=16,即可以容纳16个键值对。

2. 负载因子 —— 哈希表容量达到多少比例时就需要增加容量(在填充因子达到某一程度时,重新计算哈希表)。在Java中,默认值为 0.75f,即在填充了75%的容量后会进行重新计算哈希表的操作。

3. 阈值 —— 负载因子与初始容量的乘积。在Java中,默认值为 (16 * 0.75 = 12)。也就是说,在HashMap中插入12个键值对后就会进行重新计算哈希表的操作。

4. 重新计算哈希表 —— 当哈希表达到阈值时,将其容量加倍的操作。在Java中,默认情况下,HashMap会按照以下序列进行重新计算哈希表操作:2^4, 2^5, 2^6, 2^7, ……等等。

如果将初始容量设置得较高,则不会进行重新计算哈希表的操作。但是,由于这样会增加迭代的时间复杂度,所以必须非常灵活地选择初始容量以提高性能。需要考虑预期值的数量来设置初始容量。最常用的负载因子值为0.75,这可以在时间和空间成本之间提供很好的平衡。负载因子的值在0和1之间变化。

注意: 从Java 8开始,Java开始使用自平衡BST来代替链表进行链接。自平衡BST的优点是,它的最坏搜索时间(当每个键映射到相同的槽时)为 O(Log n)。

同步HashMap

正如我们所说,HashMap是不同步的,即多个线程可以同时访问它。如果多个线程同时访问此类并且至少有一个线程在结构上操纵它,则需要在外部将其同步。这可以通过同步封装Map的某个对象来完成。如果没有这样的对象,则可以将其包装在Collections.synchronizedMap()中,以使HashMap同步并避免意外的不同步访问。如下面的例子所示:

Map m = Collections.synchronizedMap(new HashMap(...));

现在Map m是同步的。如果在创建迭代器之后进行任何结构修改(除了通过迭代器的remove方法),此类迭代器将是快速失败的。如果迭代器出错,它将抛出ConcurrentModificationException。

HashMap的时间复杂度:

如果哈希函数正确编写并在桶之间分散元素,则HashMap提供基本操作get和put的常数时间复杂度。HashMap的迭代取决于HashMap的容量和键值对的数量,基本上与容量+大小成正比。容量是HashMap中桶的数量。因此,最好不要在HashMap中初始添加大量的桶。

HashMap的应用:

HashMap主要是hashing的实现。在需要高效的搜索、插入和删除操作时,它非常有用。请参阅哈希的应用程序以获取详细信息。

HashMap中的方法

  • K —— 映射到Map中的键的类型。
  • V —— 映射到Map中的值的类型。
方法 描述
clear() 从此地图中删除所有映射。
clone() 返回此HashMap实例的浅拷贝:键和值本身不会被克隆。
compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) 尝试计算指定密钥及其当前映射值(如果没有当前映射,则为null)的映射。
computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) 如果指定的键尚未与值相关联(或映射到null),则尝试使用给定的映射函数计算其值,并将其输入到此地图中,除非null。
computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) 如果指定键的值存在且非null,则尝试计算给定键及其当前映射值的新映射。
containsKey(Object key) 如果此映射包含特定键的映射,则返回true。
containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回true。
entrySet() 返回此映射中所包含的映射的Set视图。
get(Object key) 返回映射到指定键的值,如果此地图不包含键的映射,则返回null。
isEmpty() 如果此地图不包含键值映射,则返回true。
keySet() 返回此地图中包含的键的Set视图。
merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) 如果指定的键尚未与值相关联或与null相关联,则将其与给定的非null值相关联。
put(K key, V value) 将指定的值与该地图中指定的键相关联。
putAll(Map<? extends K, ? extends V> m) 将指定映射中的所有映射复制到此映射。
remove(Object key) 如果存在,则从此地图中删除指定键的映射。
size() 返回此地图中的键值映射数。
values() 返回此地图中包含的值的Collection视图。

继承自java.util.AbstractMap类的方法

方法 描述
equals() 将指定对象与此地图进行比较,以检查它们是否相等。
hashCode() 返回此地图的哈希码值。
toString() 返回此地图的字符串表示形式。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程