Java HashMap – Java中的哈希映射表
Java的HashMap是Java中最流行的集合类之一。Java的HashMap是基于散列表的实现。Java的HashMap继承自AbstractMap类,该类实现了Map接口。
Java 哈希映射
- Java的HashMap允许null键和null值。
HashMap不是有序集合。您可以通过键集迭代HashMap条目,但不能保证它们按照添加到HashMap中的顺序排列。
HashMap与Hashtable几乎相似,只是它是非同步的,并且允许null键和值。
HashMap使用它的内部类Node
HashMap将条目存储到多个单向链表中,称为桶或箱子。默认桶的数量是16,而且它总是2的幂。
HashMap在执行get和put操作时使用键的hashCode()和equals()方法。因此,HashMap键对象应该提供对这些方法的良好实现。这就是为什么不可变类更适合作为键的原因,例如字符串和整数。
Java的HashMap不是线程安全的,对于多线程环境,您应该使用ConcurrentHashMap类或使用Collections.synchronizedMap()方法获取同步的映射。
Java HashMap的构造函数
Java HashMap 提供了四个构造方法。
- 公开的HashMap构造函数(). 这个构造函数是最常用的HashMap构造函数. 这个构造函数将创建一个空的HashMap, 默认初始容量为16, 负载因子为0.75。
公开的HashMap构造函数(int 初始容量). 这个构造函数用于指定初始容量和0.75负载因子. 如果您知道要存储在HashMap中的映射的数量,这将有助于避免重新哈希。
公开的HashMap构造函数(int 初始容量, 浮点数 负载因子). 这个构造函数将创建一个空的HashMap, 具有指定的初始容量和负载因子. 如果您知道要存储在HashMap中的映射的最大数量, 可以使用这个构造函数. 在常见情况下,您应该避免使用它,因为负载因子0.75在空间和时间成本之间提供了良好的折衷。
公开的HashMap构造函数(Map extends K, ? extends V> m). 创建一个具有与指定映射相同映射的Map, 负载因子为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 方法
让我们来看一下HashMap在Java中的重要方法。
- public void clear():这个HashMap方法将删除所有的映射,使HashMap变为空。
public boolean containsKey(Object key):如果存在该键,则此方法返回true,否则返回false。
public boolean containsValue(Object value):如果存在该值,则此HashMap方法返回true,否则返回false。
public Set
public V get(Object key):返回与指定键相关联的值,如果键没有映射任何值,则返回null。
public boolean isEmpty():一个实用方法,如果没有键值映射,则返回true。
public 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
在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 示例
这是一个常用的HashMap方法的简单java程序。
package com.Olivia.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中是如何工作的?
Java HashMap 加载因子
负载因子用于确定何时重新哈希哈希映射表,并增加桶的大小。桶或容量的默认值是16,负载因子为0.75。重新哈希的阈值是通过将容量和负载因子相乘来计算的。因此,默认阈值为12。当哈希映射表超过12个映射时,将进行重新哈希,并将桶的数量增加到2的下一个幂即32。请注意,哈希映射表的容量始终为2的幂。默认负载因子0.75在空间和时间复杂性之间提供了良好的权衡。但是,根据您的需求,您可以将其设置为不同的值。如果您想节省空间,可以将其增加到0.80或0.90,但这样会使获取/放置操作的时间更长。
Java的HashMap的键集合
Java HashMap 的 keySet 方法返回 HashMap 中键的 Set 视图。这个 Set 视图是由 HashMap 支持的,HashMap 中的任何更改都会反映在 Set 中,反之亦然。下面是一个简单的程序示例,展示了 HashMap keySet 的用法,并介绍了如果想要一个非由 map 支持的自己的 keySet 方法应该怎么做。
package com.Olivia.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是由map支持的。
[1, 2, 3]
[1, 2, 3, 4]
{2=2, 3=3, 4=4}
[2, 3, 4]
Java HashMap values –> Java HashMap 的值
Java的HashMap的values方法返回了Map中值的一个Collection视图。这个集合是由HashMap支持的,所以HashMap中的任何变化都会在values集合中反映出来,反之亦然。下面的一个简单例子证实了HashMap values集合的这种行为。
package com.Olivia.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支持的,所以对map的任何更改都会反映在entry set中,反之亦然。请看下面的HashMap entrySet示例程序。
package com.Olivia.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方法
Java 8中介绍了HashMap putIfAbsent方法的一个简单示例。
package com.Olivia.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 方法
在Java 8中引入了HashMap的forEach方法。这是一个非常有用的方法,它可以在地图中的每个条目中执行给定的操作,直到所有条目都被处理完或操作抛出异常。
package com.Olivia.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
public class HashMapForEachExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", null);
map.put(null, "100");
BiConsumer<String, String> action = new MyBiConsumer();
map.forEach(action);
//lambda expression example
System.out.println("\nHashMap forEach lambda example\n");
map.forEach((k,v) -> {System.out.println("Key = "+k+", Value = "+v);});
}
}
class MyBiConsumer implements BiConsumer<String, String> {
@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.Olivia.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
public class HashMapReplaceAllExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put(null, "100");
System.out.println("map before replaceAll = " + map);
BiFunction<String, String, String> function = new MyBiFunction();
map.replaceAll(function);
System.out.println("map after replaceAll = " + map);
// replaceAll using lambda expressions
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<String, String, String> {
@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 可以被翻译为 “Java HashMap 的 computeIfAbsent 方法”。
HashMap 的 computeIfAbsent 方法仅在映射中不存在键时计算值。在计算完值之后,如果值不为空,则将其放入映射中。
package com.Olivia.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class HashMapComputeIfAbsent {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "10");
map.put("2", "20");
map.put(null, "100");
Function<String, String> function = new MyFunction();
map.computeIfAbsent("3", function); //key not present
map.computeIfAbsent("2", function); //key already present
//lambda way
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<String, String> {
@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。如果函数返回 null,映射将被移除。
package com.Olivia.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.Olivia.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计算示例的输出是;
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 合并
如果指定的键不存在或与null相关联,则将其与给定的非null值相关联。否则,用给定的重映射函数的结果替换相关联的值,如果结果为null,则删除。
package com.Olivia.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class HashMapMergeExample {
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);
for (Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
//merge throws NullPointerException if key or value is null
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文档。