Java HashMap示例

时间:2020-01-09 10:35:03  来源:igfitidea点击:

Java中的HashMap是Map接口的HashTable实现,它是Java Collections框架的一部分。 Java中的HashMap类扩展了AbstractMap类,并实现了Map,Cloneable和Serializable接口。

HashMap将其元素存储为(键,值)对,并且要获取值,我们将需要提供与该值配对的键。为了在HashMap中存储值,使用哈希技术,其中使用密钥计算哈希值,该哈希值决定将值存储在哪个存储区中。

HashMap的功能

本文中讨论的Java HashMap的某些功能如下:

  • 在HashMap中,值可以重复,但是键必须是唯一的。如果使用相同的密钥,则该值将被覆盖。
  • HashMap使用哈希技术存储值。
  • HashMap存储是无序的,这意味着不像ArrayList那样保持插入顺序。
  • Java中的HashMap允许空值和空键。
  • HashMap不是线程安全的。
  • 所有HashMap的"集合视图方法"返回的迭代器都是快速失败的。这意味着,如果在创建迭代器之后的任何时间对结构进行结构修改,则除了通过迭代器自己的remove方法之外,都可以通过其他方式修改该映射,否则迭代器将抛出ConcurrentModificationException。

Java HashMap构造函数

  • HashMap()–此构造函数构造一个具有默认初始容量(16)和默认加载因子(0.75)的空HashMap。

  • HashMap(int initialCapacity)–此构造函数构造一个空的HashMap,具有指定的初始容量和默认负载因子(0.75)。

  • HashMap(int initialCapacity,float loadFactor)–此构造函数构造一个具有指定初始容量和负载因子的空HashMap。

  • HashMap(Map <?扩展K ,?扩展V> m)–构造一个新的HashMap,其映射与指定的Map相同。

HashMap中的初始容量,负载因子和存储桶

Java中的HashMap在内部使用Node类型的数组存储元素。其中Node <K,V>是HashMap类中的内部类。我们应该对术语"初始容量","负载因子"和"存储桶"有清楚的了解,以便更好地了解HashMaps。

容量–如果我们在创建HashMap时未指定任何容量,则该数组的默认初始容量为16. 如果我们使用还传递了初始容量的构造函数,则该数组将具有指定的初始容量。

桶-在HashMap中使用桶的概念,因此将数组的每个索引概念化为一个桶。因此,总共有16个水桶。对于添加到HashMap的每个(键,值)对,使用该键计算哈希值,基于该哈希值选择这些存储桶之一来存储元素。这样,HashMap能够为诸如get和put之类的基本操作提供恒定的时间性能。

负载系数–负载系数是HashMap存储的阈值。一旦达到阈值,HashMap的容量就会增加一倍。默认加载因子为0.75,这意味着如果达到75%的容量,则将调整HashMap的大小。

请参考Java中的HashMap内部实现,以更好地了解HashMap在Java内部的工作方式。

创建HashMap的Java示例

此示例显示了如何创建HashMap以及如何向其中添加元素。

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class HashMapDemo {
  public static void main(String[] args) {
    // Creating HashMap
    Map<String, String> carMap = new HashMap<String, String>();
    // Storing elements
    carMap.put("1", "Audi");
    carMap.put("2", "BMW");
    carMap.put("3", "Jaguar");
    carMap.put(null, "Volga");
    carMap.put(null, "Volks Wagon");
    carMap.put("4", null);
    carMap.put("3", "Mini Cooper");
        
    Set<String> carSet =  carMap.keySet();
    for(String key : carSet){
      System.out.println("Key is " + key + " Value is " + carMap.get(key));
    }
  }
}

输出:

Key is null Value is Volks Wagon
Key is 1 Value is Audi
Key is 2 Value is BMW
Key is 3 Value is Mini Cooper
Key is 4 Value is null

在代码中,使用此语句创建默认容量的HashMap。

Map<String, String> carMap = new HashMap<String, String>();

现在,所有Collection类都是通用的,因此我们可以在开始时指定要在Map中存储的元素类型。本示例中使用的Map只能存储字符串作为键和值。

从输出中,我们可以看到上面已经提到的一些要点。

  • HashMap中不维护插入顺序。值未按插入顺序显示。

  • 将插入两个以null为键的值,第二次插入将覆盖第一个值,因为Java HashMap中仅允许使用一个null键。

  • 还插入一个空值。

  • 使用相同的键" 3"插入两个值。如果密钥相同,第二个插入将覆盖第一个。

HashMap类中的方法

这是Java HashMap类中一些方法的列表。

  • put(K键,V值)–将指定值与此映射中的指定键关联。

  • putAll(Map <?扩展K ,?扩展V> m)–将所有映射从指定映射复制到此映射。

  • get(Object key)–返回指定键所映射到的值;如果此映射不包含该键的映射,则返回null。

  • containsKey(Object key)–如果此映射包含指定键的映射,则返回true。

  • containsValue(Object value)–如果此映射将一个或者多个键映射到指定值,则返回true。

  • remove(Object key)–从此映射中删除指定键的映射(如果存在)。

  • clear()–从此映射中删除所有映射。

  • entrySet()–返回此映射中包含的映射的Set视图。

  • keySet()–返回此映射中包含的键的Set视图。

  • values()–返回此映射中包含的值的Collection视图。

  • size()–返回此映射中的键值映射数。

  • isEmpty()–如果此映射不包含键值映射,则返回true。

  • compute(K键,BiFunction <?super K,?super V ,?扩展V> remappingFunction)–尝试计算指定键及其当前映射值的映射(如果没有当前映射,则为null)。

  • computeIfAbsent(K键,Function <?super K ,?扩展V> mappingFunction)–如果指定的键尚未与值关联(或者映射为null),则尝试使用给定的映射函数计算其值并输入进入此地图,除非为null。

  • computeIfPresent(K键,BiFunction <?超级K ,?超级V ,?扩展V> remappingFunction)–如果存在指定键的值且该值不为空,则尝试计算给定键及其当前映射值的新映射。

Java示例从HashMap中删除和替换元素

public class HashMapDemo {
  public static void main(String[] args) {
    // Creating HashMap
    Map<String, String> carMap = new HashMap<String, String>();
    // Storing elements
    carMap.put("1", "Audi");
    carMap.put("2", "BMW");
    carMap.put("3", "Jaguar");
    carMap.put("4", "Mini Cooper");
    // removing element
    carMap.remove("2");
        
    // replacing element
    carMap.replace("3", "Land Rover");
    Set<String> carSet =  carMap.keySet();
    for(String key : carSet){
        System.out.println("Key is " + key + " Value is " + carMap.get(key));
    }
  }
}

输出:

Key is 1 Value is Audi
Key is 3 Value is Land Rover
Key is 4 Value is Mini Cooper

使用lambdas的computeIfPresent和computeIfAbsent示例

public class HashMapDemo {
  public static void main(String[] args) {
    // Creating HashMap
    Map<String, String> carMap = new HashMap<String, String>();
    // Storing elements
    carMap.put("1", "Audi");
    carMap.put("2", "BMW");
    carMap.put("3", "Jaguar");
    carMap.put("4", "Mini Cooper");
    // returns value for new key
    carMap.computeIfAbsent("5", k -> {return "Land Rover";});
    // change value for existing key
    carMap.computeIfPresent("4", (String k, String v) -> {
    if (carMap.get(k).equals("Mini Cooper")){
        return "Mazda";} 
    else
        return v;});
        
    Set<String> carSet =  carMap.keySet();
    for(String key : carSet){
      System.out.println("Key is " + key + " Value is " + carMap.get(key));
    }
  }
}

输出:

Key is 1 Value is Audi
Key is 2 Value is BMW
Key is 3 Value is Jaguar
Key is 4 Value is Mazda
Key is 5 Value is Land Rover

Java HashMap迭代器示例

我们不能在HashMap中直接使用迭代器。我们将必须获取Map的集合视图,然后对其进行迭代。由迭代器方法返回的迭代器是快速失败的。如果在创建迭代器之后的任何时候修改了Map,则除了通过迭代器自己的remove方法以其他方式进行修改外,迭代器都会引发ConcurrentModificationException。

请参阅用Java迭代HashMap的不同方法,以查看用于迭代HahsMap的选项。

让我们尝试举例说明一下。在代码中,使用keySet()迭代HashMap的设置视图后,我们将尝试使用HashMap的remove()方法(而不是迭代器的remove方法)来删除元素。所有这些方法,例如entrySet()或者keySet()都是快速失败的。这意味着,如果在创建迭代器之后的任何时间对结构进行结构修改,则除了通过迭代器自己的remove方法之外,都可以通过其他方式修改该映射,否则迭代器将抛出ConcurrentModificationException。

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class HashMapDemo {
  public static void main(String[] args) {
    // Creating HashMap
    Map<String, String> carMap = new HashMap<String, String>();
    // Storing elements
    carMap.put("1", "Audi");
    carMap.put("2", "BMW");
    carMap.put("3", "Jaguar");
    carMap.put("4", "Mini Cooper");
        
    Set<String> carSet =  carMap.keySet();
    Iterator<String> itr = carSet.iterator();
    while (itr.hasNext()) {
      String key = itr.next();
      System.out.println("Key is " + key + " Value is " + carMap.get(key));    
      // removing value using HashMap's remove method
      if(key.equals("2")){
          carMap.remove(key);
      }
    }
  }
}

输出:

Key is 1 Value is Audi
Key is 2 Value is BMW
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(Unknown Source)
	at java.util.HashMap$KeyIterator.next(Unknown Source)
	at com.theitroad.HashMapDemo.main(HashMapDemo.java:22)

如我们所见,当我们尝试使用迭代器对HashMap进行结构化修改时,会抛出ConcurrentModificationException异常。

使用迭代器的remove方法

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class HashMapDemo {
  public static void main(String[] args) {
    // Creating HashMap
    Map<String, String> carMap = new HashMap<String, String>();
    // Storing elements
    carMap.put("1", "Audi");
    carMap.put("2", "BMW");
    carMap.put("3", "Jaguar");
    carMap.put("4", "Mini Cooper");
        
    Set<String> carSet =  carMap.keySet();
    Iterator<String> itr = carSet.iterator();
    while (itr.hasNext()) {
      String key = itr.next();
      System.out.println("Key is " + key + " Value is " + carMap.get(key));    
      // removing value using HashMap's remove method
      if(key.equals("2")){
          itr.remove();
      }
    }
        
    System.out.println("** After element removal **");
    for(String key : carMap.keySet()){
      System.out.println("Key is " + key + " Value is " + carMap.get(key));
    }
  }
}

输出:

Key is 1 Value is Audi
Key is 2 Value is BMW
Key is 3 Value is Jaguar
Key is 4 Value is Mini Cooper
** After element removal **
Key is 1 Value is Audi
Key is 3 Value is Jaguar
Key is 4 Value is Mini Cooper

HashMap不是线程安全的

Java中的HashMap不是线程安全的。如果在多线程环境中使用HashMap,在该环境中HashMap的实例在许多线程之间共享,则应在外部进行同步。为了同步Map,可以使用Collections.synchronizedMap()方法,该方法返回由指定map支持的同步Map。

例如

Map<String, String> tempMap = Collections.synchronizedMap(carMap);