Java HashMap 是 Java 中最受欢迎的 Collection 类之一。Java HashMap 是基于哈希表的实现。HashMap 在 Java 中扩展了 AbstractMap 类,该类实现了 Map 接口。
Java HashMap
关于 Java 中 HashMap 的一些重要点包括;
- Java HashMap 允许 null 键和 null 值。
- HashMap 不是有序集合。您可以通过键集迭代 HashMap 条目,但不能保证它们按照添加到 HashMap 中的顺序排列。
- HashMap 几乎与 Hashtable 相似,除了它是不同步的,并且允许 null 键和值。
- HashMap 使用它的内部类 Node<K,V> 来存储映射条目。
- HashMap 将条目存储到多个称为桶或箱子的单向链表中。默认桶的数量是 16,而且始终是 2 的幂。
- HashMap 在获取和放置操作中使用键的 hashCode() 和 equals() 方法。因此,HashMap 键对象应提供这些方法的良好实现。这就是为什么不可变类更适合用作键的原因,例如 String 和 Interger。
- Java HashMap 不是线程安全的,对于多线程环境,您应该使用 ConcurrentHashMap 类或使用
Collections.synchronizedMap()
方法获取同步的映射。
Java HashMap 构造函数
Java HashMap 提供了四个构造函数。
- public HashMap(): 最常用的 HashMap 构造函数。此构造函数将创建一个空的 HashMap,其默认初始容量为 16,负载因子为 0.75。
- public HashMap(int initialCapacity): 此 HashMap 构造函数用于指定初始容量和 0.75 的负载因子。如果您知道要存储在 HashMap 中的映射数量,则使用此构造函数可避免重新哈希。
- public HashMap(int initialCapacity, float loadFactor): 此 HashMap 构造函数将创建一个具有指定初始容量和负载因子的空 HashMap。如果您知道要存储在 HashMap 中的最大映射数量,则可以使用此构造函数。在通常情况下,应避免使用此构造函数,因为负载因子 0.75 在空间和时间成本之间提供了良好的权衡。
- public HashMap(Map<? extends K, ? extends V> m): 创建一个具有与指定地图相同映射的地图,并且负载因子为 0.75。
Java HashMap 构造函数示例
以下代码片段显示了使用所有上述构造函数的 HashMap 示例。
Map<String, String> map1 = new HashMap<>();
Map<String, String> map2 = new HashMap<>(2^5);
Map<String, String> map3 = new HashMap<>(32,0.80f);
Map<String,String> map4 = new HashMap<>(map1);
Java HashMap 方法
让我们来看看 Java 中 HashMap 的重要方法。
- public void clear(): 这个 HashMap 方法将删除所有映射,HashMap 将变为空。
- public boolean containsKey(Object key): 如果键存在则返回 ‘true’,否则返回 ‘false’。
- public boolean containsValue(Object value): 如果值存在则返回 ‘true’,否则返回 ‘false’。
- public Set<Map.Entry<K,V>> entrySet(): 此方法返回 HashMap 映射的 Set 视图。此集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。
- public V get(Object key): 返回指定键映射的值;如果没有该键的映射,则返回 null。
- public boolean isEmpty(): 一个实用方法,如果没有键值映射存在则返回 true。
- public Set<K> keySet(): 返回此映射中包含的键的 Set 视图。该集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。
- public V put(K key, V value): 在此地图中将指定的值与指定的键相关联。如果地图先前包含键的映射,则旧值将被替换。
- public void putAll(Map extends K, ? extends V> m): 将指定映射中的所有映射复制到此映射中。这些映射将替换此映射对当前指定映射中任何键的任何映射。
- public V remove(Object key): 如果存在,则从此映射中删除指定键的映射。
- public int size(): 返回此映射中的键-值映射数。
- public Collection
values() : 返回此映射中包含的值的 Collection 视图。该集合由映射支持,因此对映射的更改会反映在集合中,反之亦然。
Java 8 中 HashMap 中引入了许多新方法。
- public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction): 如果指定键尚未与值关联(或映射为 null),则此方法尝试使用给定的映射函数计算其值,并将其输入到 HashMap 中,除非为 Null。
- public V computeIfPresent(K key, BiFunction super K, ? super V, ? extends V> remappingFunction): 如果存在指定键的值且不为 null,则尝试计算给定键及其当前映射值的新映射。
- public V compute(K key, BiFunction super K, ? super V, ? extends V> remappingFunction): 此 HashMap 方法尝试计算指定键及其当前映射值的映射。
- public void forEach(BiConsumer<? super K, ? super V> action): 此方法对映射中的每个条目执行给定的操作。
- public V getOrDefault(Object key, V defaultValue): 与 get 方法相同,但如果未找到指定键的映射,则返回 defaultValue。
- public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction): 如果指定键尚未与值关联或与 null 关联,则将其与给定的非 null 值关联。否则,用给定重映射函数的结果替换关联值,如果结果为 null,则删除关联值。
- public V putIfAbsent(K key, V value): 如果指定键尚未与值关联(或映射为 null),则将其与给定值关联并返回 null,否则返回当前值。
- public boolean remove(Object key, Object value): 仅当指定键当前映射到指定值时才删除该键的条目。
- public boolean replace(K key, V oldValue, V newValue): 仅当指定键当前映射到指定值时才替换该键的条目。
- public V replace(K key, V value): 仅当指定键当前映射到某个值时才替换该键的条目。
- public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function): 用调用给定函数在条目上的结果替换每个条目的值。
Java HashMap 示例
这里是一个简单的 Java 程序,用于展示 HashMap 常用方法。
package com.journaldev.examples;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1"); // put example
map.put("2", "2");
map.put("3", "3");
map.put("4", null); // null value
map.put(null, "100"); // null key
String value = map.get("3"); // get example
System.out.println("Key = 3, Value = " + value);
value = map.getOrDefault("5", "Default Value");
System.out.println("Key = 5, Value=" + value);
boolean keyExists = map.containsKey(null);
boolean valueExists = map.containsValue("100");
System.out.println("keyExists=" + keyExists + ", valueExists=" + valueExists);
Set<Entry<String, String>> entrySet = map.entrySet();
System.out.println(entrySet);
System.out.println("map size=" + map.size());
Map<String, String> map1 = new HashMap<>();
map1.putAll(map);
System.out.println("map1 mappings= " + map1);
String nullKeyValue = map1.remove(null);
System.out.println("map1 null key value = " + nullKeyValue);
System.out.println("map1 after removing null key = " + map1);
Set<String> keySet = map.keySet();
System.out.println("map keys = " + keySet);
Collection<String> values = map.values();
System.out.println("map values = " + values);
map.clear();
System.out.println("map is empty=" + map.isEmpty());
}
}
以下是上述 Java HashMap 示例程序的输出。
Key = 3, Value = 3
Key = 5, Value=Default Value
keyExists=true, valueExists=true
[null=100, 1=1, 2=2, 3=3, 4=null]
map size=5
map1 mappings= {null=100, 1=1, 2=2, 3=3, 4=null}
map1 null key value = 100
map1 after removing null key = {1=1, 2=2, 3=3, 4=null}
map keys = [null, 1, 2, 3, 4]
map values = [100, 1, 2, 3, null]
map is empty=true
HashMap 在 Java 中如何工作?
HashMap在Java中使用其内部类Node<K,V>来存储映射。HashMap基于哈希算法,使用键的hashCode()和equals()方法进行获取和放置操作。HashMap使用单链表来存储元素,这些称为bins或buckets。当我们调用put方法时,将使用键的hashCode确定用于存储映射的bucket。一旦标识了bucket,将使用hashCode检查是否已经存在具有相同hashCode的键。如果存在具有相同hashCode的现有键,则在键上使用equals()方法。如果equals返回true,则覆盖值,否则将新映射添加到此单链表bucket中。如果没有具有相同hashCode的键,则将映射插入到bucket中。对于HashMap的get操作,再次使用键的hashCode来确定要查找值的bucket。确定了bucket后,将遍历条目以使用hashCode和equals方法找到Entry。如果找到匹配,则返回值,否则返回null。还涉及许多其他事项,如使用哈希算法获取键的bucket,映射的重新散列等。但对于我们的工作,只需记住HashMap操作基于Key,需要良好实现的hashCode和equals方法以避免不必要的行为。下面的图片显示了get和put操作的说明。 推荐阅读: Java中hashCode和equals方法的重要性
Java HashMap 加载因子
加载因子用于确定何时对 HashMap 进行重新哈希并增加存储桶的大小。桶或容量的默认值为16,加载因子为0.75。重新哈希的阈值通过将容量和加载因子相乘来计算。因此,默认阈值为12。因此,当 HashMap 具有超过12个映射时,将进行重新哈希,并且桶的数量将增加到下一个2的幂,即32。请注意,HashMap 的容量始终是2的幂。默认加载因子为0.75在空间和时间复杂性之间提供了良好的平衡。但是,根据需要,您可以将其设置为不同的值。如果要节省空间,可以将其值增加到0.80或0.90,但是获取/放置操作将需要更多时间。
Java HashMap keySet
Java HashMap keySet 方法返回 HashMap 中键的 Set 视图。此 Set 视图由 HashMap 支持,HashMap 中的任何更改都会反映在 Set 中,反之亦然。下面是演示 HashMap keySet 示例的简单程序,以及如果您需要一个不受地图支持的 keySet,则应采取的方法。
package com.journaldev.examples;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class HashMapKeySetExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
Set<String> keySet = map.keySet();
System.out.println(keySet);
map.put("4", "4");
System.out.println(keySet); // keySet is backed by Map
keySet.remove("1");
System.out.println(map); // map is also modified
keySet = new HashSet<>(map.keySet()); // copies the key to new Set
map.put("5", "5");
System.out.println(keySet); // keySet is not modified
}
}
上述程序的输出将清楚地表明,keySet 受地图支持。
[1, 2, 3]
[1, 2, 3, 4]
{2=2, 3=3, 4=4}
[2, 3, 4]
Java HashMap 值
Java HashMap 的 values 方法返回 Map 中值的 Collection 视图。此 Collection 受 HashMap 支持,因此对 HashMap 的任何更改都会反映在值集合中,反之亦然。下面的简单示例证实了 HashMap 值集合的这种行为。
package com.journaldev.examples;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class HashMapValuesExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", null);
map.put("4", null);
map.put(null, "100");
Collection<String> values = map.values();
System.out.println("map values = " + values);
map.remove(null);
System.out.println("map values after removing null key = " + values);
map.put("5", "5");
System.out.println("map values after put = " + values);
System.out.println(map);
values.remove("1"); // changing values collection
System.out.println(map); // updates in map too
}
}
上述程序的输出如下。
map values = [100, 1, 2, null, null]
map values after removing null key = [1, 2, null, null]
map values after put = [1, 2, null, null, 5]
{1=1, 2=2, 3=null, 4=null, 5=5}
{2=2, 3=null, 4=null, 5=5}
Java HashMap entrySet
Java HashMap 的 entrySet 方法返回映射的 Set 视图。这个 entrySet 受 HashMap 支持,因此对映射的任何更改都会反映在 entry set 中,反之亦然。请查看下面的 HashMap entrySet 示例程序。
package com.journaldev.examples;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapEntrySetExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", null);
map.put(null, "100");
Set<Entry<String,String>> entrySet = map.entrySet();
Iterator<Entry<String, String>> iterator = entrySet.iterator();
Entry<String, String> next = null;
System.out.println("map before processing = "+map);
System.out.println("entrySet before processing = "+entrySet);
while(iterator.hasNext()){
next = iterator.next();
System.out.println("Processing on: "+next.getValue());
if(next.getKey() == null) iterator.remove();
}
System.out.println("map after processing = "+map);
System.out.println("entrySet after processing = "+entrySet);
Entry<String, String> simpleEntry = new AbstractMap.SimpleEntry<String, String>("1","1");
entrySet.remove(simpleEntry);
System.out.println("map after removing Entry = "+map);
System.out.println("entrySet after removing Entry = "+entrySet);
}
}
上述程序的输出如下。
map before processing = {null=100, 1=1, 2=null}
entrySet before processing = [null=100, 1=1, 2=null]
Processing on: 100
Processing on: 1
Processing on: null
map after processing = {1=1, 2=null}
entrySet after processing = [1=1, 2=null]
map after removing Entry = {2=null}
entrySet after removing Entry = [2=null]
Java HashMap putIfAbsent
A simple example for HashMap putIfAbsent method introduced in Java 8.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
public class HashMapPutIfAbsentExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", null);
map.put(null, "100");
System.out.println("map before putIfAbsent = "+map);
String value = map.putIfAbsent("1", "4");
System.out.println("map after putIfAbsent = "+map);
System.out.println("putIfAbsent returns: "+value);
System.out.println("map before putIfAbsent = "+map);
value = map.putIfAbsent("3", "3");
System.out.println("map after putIfAbsent = "+map);
System.out.println("putIfAbsent returns: "+value);
}
}
上述程序的输出是:
map before putIfAbsent = {null=100, 1=1, 2=null}
map after putIfAbsent = {null=100, 1=1, 2=null}
putIfAbsent returns: 1
map before putIfAbsent = {null=100, 1=1, 2=null}
map after putIfAbsent = {null=100, 1=1, 2=null, 3=3}
putIfAbsent returns: null
Java HashMap forEach
HashMap forEach方法在Java 8中被引入。这是一个非常有用的方法,用于对映射中的每个条目执行给定的操作,直到处理完所有条目或操作引发异常为止。
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
public class HashMapForEachExample {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "1");
map.put("2", null);
map.put(null, "100");
BiConsumer action = new MyBiConsumer();
map.forEach(action);
//lambda表达式示例
System.out.println("\nHashMap forEach lambda example\n");
map.forEach((k,v) -> {System.out.println("Key = "+k+", Value = "+v);});
}
}
class MyBiConsumer implements BiConsumer {
@Override
public void accept(String t, String u) {
System.out.println("Key = " + t);
System.out.println("Processing on value = " + u);
}
}
上述HashMap forEach示例程序的输出是:
Key = null
Processing on value = 100
Key = 1
Processing on value = 1
Key = 2
Processing on value = null
HashMap forEach lambda example
Key = null, Value = 100
Key = 1, Value = 1
Key = 2, Value = null
Java HashMap replaceAll
HashMap replaceAll方法可用于使用在该条目上调用给定函数的结果替换每个条目的值。此方法在Java 8中添加,我们可以使用lambda表达式作为该方法的参数。
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
public class HashMapReplaceAllExample {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put(null, "100");
System.out.println("map before replaceAll = " + map);
BiFunction function = new MyBiFunction();
map.replaceAll(function);
System.out.println("map after replaceAll = " + map);
//使用lambda表达式的replaceAll
map.replaceAll((k, v) -> {
if (k != null) return k + v;
else return v;});
System.out.println("map after replaceAll lambda expression = " + map);
}
}
class MyBiFunction implements BiFunction {
@Override
public String apply(String t, String u) {
if (t != null)
return t + u;
else
return u;
}
}
上述HashMap replaceAll程序的输出是:
map before replaceAll = {null=100, 1=1, 2=2}
map after replaceAll = {null=100, 1=11, 2=22}
map after replaceAll lambda example = {null=100, 1=111, 2=222}
Java HashMap computeIfAbsent
HashMap computeIfAbsent方法仅在映射中不存在键时计算该值。计算该值后,如果不为null,则将其放入映射中。
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class HashMapComputeIfAbsent {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "10");
map.put("2", "20");
map.put(null, "100");
Function function = new MyFunction();
map.computeIfAbsent("3", function); //key not present
map.computeIfAbsent("2", function); //key already present
//lambda方式
map.computeIfAbsent("4", v -> {return v;});
map.computeIfAbsent("5", v -> {return null;}); //null value won't get inserted
System.out.println(map);
}
}
class MyFunction implements Function {
@Override
public String apply(String t) {
return t;
}
}
上述程序的输出是;
{null=100, 1=10, 2=20, 3=3, 4=4}
Java HashMap computeIfPresent
Java HashMap的computeIfPresent方法在指定键存在且值非空时重新计算值。如果函数返回null,则移除映射。
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
public class HashMapComputeIfPresentExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "10");
map.put("2", "20");
map.put(null, "100");
map.put("10", null);
System.out.println("map before computeIfPresent = " + map);
BiFunction<String, String, String> function = new MyBiFunction1();
for (String key : map.keySet()) {
map.computeIfPresent(key, function);
}
System.out.println("map after computeIfPresent = " + map);
map.computeIfPresent("1", (k,v) -> {return null;}); // mapping will be removed
System.out.println("map after computeIfPresent = " + map);
}
}
class MyBiFunction1 implements BiFunction<String, String, String> {
@Override
public String apply(String t, String u) {
return t + u;
}
}
HashMap computeIfPresent示例的输出是;
map before computeIfPresent = {null=100, 1=10, 2=20, 10=null}
map after computeIfPresent = {null=null100, 1=110, 2=220, 10=null}
map after computeIfPresent = {null=null100, 2=220, 10=null}
Java HashMap compute
如果要基于其键和值对所有映射应用函数,则应使用compute方法。如果没有映射并且使用了此方法,则compute函数的值将为null。
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
public class HashMapComputeExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put(null, "10");
map.put("10", null);
System.out.println("map before compute = "+map);
for (String key : map.keySet()) {
map.compute(key, (k,v) -> {return k+v;});
}
map.compute("5", (k,v) -> {return k+v;}); //key not present, v = null
System.out.println("map after compute = "+map);
}
}
HashMap compute示例的输出是;
map before compute = {null=10, 1=1, 2=2, 10=null}
map after compute = {null=null10, 1=11, 2=22, 5=5null, 10=10null}
Java HashMap merge
如果指定的键不存在或与null关联,则将其与给定的非null值关联。否则,将关联的值替换为给定重新映射函数的结果,如果结果为null,则删除。
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class HashMapMergeExample {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put(null, "10");
map.put("10", null);
for (Entry entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
//如果键或值为null,则merge会抛出NullPointerException
if(key != null && value != null)
map.merge(entry.getKey(), entry.getValue(),
(k, v) -> {return k + v;});
}
System.out.println(map);
map.merge("5", "5", (k, v) -> {return k + v;}); // key not present
System.out.println(map);
map.merge("1", "1", (k, v) -> {return null;}); // method return null, so remove
System.out.println(map);
}
}
上述程序的输出为;
{null=10, 1=11, 2=22, 10=null}
{null=10, 1=11, 2=22, 5=5, 10=null}
{null=10, 2=22, 5=5, 10=null}
这就是关于Java中HashMap的全部内容,希望没有漏掉重要的东西。如果你喜欢的话,也可以和其他人分享。参考:API 文档
Source:
https://www.digitalocean.com/community/tutorials/java-hashmap