Java HashMap – Java中的哈希映射表

Java的HashMap是Java中最流行的集合类之一。Java的HashMap是基于散列表的实现。Java的HashMap继承自AbstractMap类,该类实现了Map接口。

Java 哈希映射

java hashmap, hashmap in java, java hashmap example, java hashmap tutorial
    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 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> entrySet():此方法返回HashMap映射的Set视图。该集合是由映射支持的,因此对映射的更改会反映在集合中,反之亦然。

    public V get(Object key):返回与指定键相关联的值,如果键没有映射任何值,则返回null。

    public boolean isEmpty():一个实用方法,如果没有键值映射,则返回true。

    public Set keySet():返回此映射中包含的键的Set视图。该集合是由映射支持的,因此对映射的更改会反映在集合中,反之亦然。

    public V put(K key, V value):将指定值与指定键在此映射中关联。如果映射先前包含键的映射,则旧值将被替换。

    public void putAll(Map m):将指定映射中的所有映射复制到此映射中。这些映射将替换该映射中当前存在的任何键的映射。

    public V remove(Object key):如果存在,则从该映射中删除指定键的映射。

    public int size():返回该映射中的键值映射数。

    public Collection values():返回此映射中包含的值的集合视图。该集合是由映射支持的,因此对映射的更改会反映在集合中,反之亦然。

在Java 8中,HashMap引入了许多新的方法。

    public V computeIfAbsent(K key, Function mappingFunction): 如果指定的键尚未与值关联(或者与null关联),该方法将尝试使用给定的映射函数计算其值,并将其输入到HashMap中,除非值为Null。
    public V computeIfPresent(K key, BiFunction remappingFunction): 如果指定键的值存在且非null,则尝试使用键和其当前映射值计算新映射。
    public V compute(K key, BiFunction remappingFunction): 该HashMap方法尝试为指定的键和其当前映射值计算映射。
    public void forEach(BiConsumer action): 该方法对该映射中的每个条目执行给定的操作。
    public V getOrDefault(Object key, V defaultValue): 与get方法相同,只是如果未找到指定键的映射,则返回defaultValue。
    public V merge(K key, V value, BiFunction 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 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 internal implementation, how hashmap works in 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文档。

发表回复 0

Your email address will not be published. Required fields are marked *