Java HashMap示例
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);