Java HashMap详解:原理、用法与实例教程

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

Java 哈希映射

java hashmap, hashmap in java, java hashmap example, java hashmap tutorial
  1. Java的HashMap允许null键和null值。
  2. HashMap不是有序集合。您可以通过键集迭代HashMap条目,但不能保证它们按照添加到HashMap中的顺序排列。
  3. HashMap与Hashtable几乎相似,只是它是非同步的,并且允许null键和值。
  4. HashMap使用它的内部类Node<K,V>来存储映射条目。
  5. HashMap将条目存储到多个单向链表中,称为桶或箱子。默认桶的数量是16,而且它总是2的幂。
  6. HashMap在执行get和put操作时使用键的hashCode()和equals()方法。因此,HashMap键对象应该提供对这些方法的良好实现。这就是为什么不可变类更适合作为键的原因,例如字符串和整数。
  7. Java的HashMap不是线程安全的,对于多线程环境,您应该使用ConcurrentHashMap类或使用Collections.synchronizedMap()方法获取同步的映射。

Java HashMap的构造函数

Java HashMap 提供了四个构造方法。

  1. public HashMap():这是最常用的HashMap构造函数。这个构造函数将创建一个空的HashMap,默认初始容量为16,负载因子为0.75。
  2. public HashMap(int initialCapacity):这个构造函数用于指定初始容量和0.75负载因子。如果您知道要存储在HashMap中的映射的数量,这将有助于避免重新哈希。
  3. public HashMap(int initialCapacity, float loadFactor):这个构造函数将创建一个空的HashMap,具有指定的初始容量和负载因子。如果您知道要存储在HashMap中的映射的最大数量,可以使用这个构造函数。在常见情况下,您应该避免使用它,因为负载因子0.75在空间和时间成本之间提供了良好的折衷。
  4. public 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中的重要方法。

  1. public void clear():这个HashMap方法将删除所有的映射,使HashMap变为空。
  2. public boolean containsKey(Object key):如果存在该键,则此方法返回true,否则返回false。
  3. public boolean containsValue(Object value):如果存在该值,则此HashMap方法返回true,否则返回false。
  4. public Set<Map.Entry<K,V>> entrySet():此方法返回HashMap映射的Set视图。该集合是由映射支持的,因此对映射的更改会反映在集合中,反之亦然。
  5. public V get(Object key):返回与指定键相关联的值,如果键没有映射任何值,则返回null。
  6. public boolean isEmpty():一个实用方法,如果没有键值映射,则返回true。
  7. public Set<K> keySet():返回此映射中包含的键的Set视图。该集合是由映射支持的,因此对映射的更改会反映在集合中,反之亦然。
  8. public V put(K key, V value):将指定值与指定键在此映射中关联。如果映射先前包含键的映射,则旧值将被替换。
  9. public void putAll(Map<? extends K, ? extends V> m):将指定映射中的所有映射复制到此映射中。这些映射将替换该映射中当前存在的任何键的映射。
  10. public V remove(Object key):如果存在,则从该映射中删除指定键的映射。
  11. public int size():返回该映射中的键值映射数。
  12. public Collection<V> values():返回此映射中包含的值的集合视图。该集合是由映射支持的,因此对映射的更改会反映在集合中,反之亦然。

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

  1. public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction):如果指定的键尚未与值关联(或者与null关联),该方法将尝试使用给定的映射函数计算其值,并将其输入到HashMap中,除非值为Null。
  2. public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction):如果指定键的值存在且非null,则尝试使用键和其当前映射值计算新映射。
  3. public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction):该HashMap方法尝试为指定的键和其当前映射值计算映射。
  4. public void forEach(BiConsumer<? super K, ? super V> action):该方法对该映射中的每个条目执行给定的操作。
  5. public V getOrDefault(Object key, V defaultValue):与get方法相同,只是如果未找到指定键的映射,则返回defaultValue。
  6. public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction):如果指定的键尚未与值关联或者与null关联,则将其与给定的非null值关联。否则,用给定的重新映射函数的结果替换关联的值,如果结果为null,则移除。
  7. public V putIfAbsent(K key, V value):如果指定的键尚未与值关联(或者与null关联),将其与给定值关联,并返回null;否则返回当前值。
  8. public boolean remove(Object key, Object value):仅在指定键当前映射到指定值时才删除该条目。
  9. public boolean replace(K key, V oldValue, V newValue):仅当指定键当前映射到指定值时,才替换该条目。
  10. public V replace(K key, V value):仅当指定键当前映射到某个值时,才替换该条目。
  11. public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function):使用给定函数对每个条目的值进行替换。

Java HashMap 示例

这是一个常用的HashMap方法的简单Java程序。

Java HashMap示例代码

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示例
		map.put("2", "2");
		map.put("3", "3");
		map.put("4", null); // null值
		map.put(null, "100"); // null键

		String value = map.get("3"); // get示例
		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内部实现,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由Map支持

		keySet.remove("1");
		System.out.println(map); // map也被修改

		keySet = new HashSet<>(map.keySet()); // 将键复制到新的Set中
		map.put("5", "5");
		System.out.println(keySet); // keySet未被修改
	}

}

以上程序的输出清楚地表明了keySet是由map支持的。

[1, 2, 3]
[1, 2, 3, 4]
{2=2, 3=3, 4=4}
[2, 3, 4]

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"); // 更改values集合
		System.out.println(map); // map也被更新

	}

}

以上程序的输出如下。

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示例程序。

这是文章《Java HashMap – Java中的哈希映射表》的第3部分(共5部分)。

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 = "+map);
		System.out.println("处理前的entrySet = "+entrySet);
		while(iterator.hasNext()){
			next = iterator.next();
			System.out.println("正在处理: "+next.getValue());
			if(next.getKey() == null) iterator.remove();
		}
		
		System.out.println("处理后的map = "+map);
		System.out.println("处理后的entrySet = "+entrySet);
		
		Entry<String, String> simpleEntry = new AbstractMap.SimpleEntry<String, String>("1","1");
		entrySet.remove(simpleEntry);
		System.out.println("移除Entry后的map = "+map);
		System.out.println("移除Entry后的entrySet = "+entrySet);
	}

}

以下是由上述程序生成的输出结果。

处理前的map = {null=100, 1=1, 2=null}
处理前的entrySet = [null=100, 1=1, 2=null]
正在处理: 100
正在处理: 1
正在处理: null
处理后的map = {1=1, 2=null}
处理后的entrySet = [1=1, 2=null]
移除Entry后的map = {2=null}
移除Entry后的entrySet = [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("putIfAbsent操作前的map = "+map);
		String value = map.putIfAbsent("1", "4");
		System.out.println("putIfAbsent操作后的map = "+map);
		System.out.println("putIfAbsent返回值: "+value);
		
		System.out.println("putIfAbsent操作前的map = "+map);
		value = map.putIfAbsent("3", "3");
		System.out.println("putIfAbsent操作后的map = "+map);
		System.out.println("putIfAbsent返回值: "+value);
	}

}

上述程序的输出是:

putIfAbsent操作前的map = {null=100, 1=1, 2=null}
putIfAbsent操作后的map = {null=100, 1=1, 2=null}
putIfAbsent返回值: 1
putIfAbsent操作前的map = {null=100, 1=1, 2=null}
putIfAbsent操作后的map = {null=100, 1=1, 2=null, 3=3}
putIfAbsent返回值: 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表达式示例
		System.out.println("\nHashMap forEach lambda表达式示例\n");
		map.forEach((k,v) -> {System.out.println("键 = "+k+", 值 = "+v);});
	}

}

class MyBiConsumer implements BiConsumer<String, String> {

	@Override
	public void accept(String t, String u) {
		System.out.println("键 = " + t);
		System.out.println("正在处理的值 = " + u);
	}

}

以上HashMap forEach示例程序的输出是:

键 = null
正在处理的值 = 100
键 = 1
正在处理的值 = 1
键 = 2
正在处理的值 = null

HashMap forEach lambda表达式示例

键 = null, 值 = 100
键 = 1, 值 = 1
键 = 2, 值 = 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("replaceAll之前的映射 = " + map);
        BiFunction<String, String, String> function = new MyBiFunction();
        map.replaceAll(function);
        System.out.println("replaceAll之后的映射 = " + map);

        // 使用lambda表达式的replaceAll
        map.replaceAll((k, v) -> {
            if (k != null) return k + v;
            else return v;});
        System.out.println("使用lambda表达式replaceAll后的映射 = " + 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程序的输出是:

replaceAll之前的映射 = {null=100, 1=1, 2=2}
replaceAll之后的映射 = {null=100, 1=11, 2=22}
使用lambda表达式replaceAll后的映射 = {null=100, 1=111, 2=222}

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); // 键不存在
        map.computeIfAbsent("2", function); // 键已存在
        
        // lambda方式
        map.computeIfAbsent("4", v -> {return v;});
        map.computeIfAbsent("5", v -> {return null;}); // null值不会被插入
        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("computeIfPresent之前的映射 = " + map);
        BiFunction<String, String, String> function = new MyBiFunction1();
        for (String key : map.keySet()) {
            map.computeIfPresent(key, function);
        }
        
        System.out.println("computeIfPresent之后的映射 = " + map);
        map.computeIfPresent("1", (k,v) -> {return null;}); // 映射将被移除
        System.out.println("computeIfPresent之后的映射 = " + map);

    }

}

class MyBiFunction1 implements BiFunction<String, String, String> {

    @Override
    public String apply(String t, String u) {
        return t + u;
    }

}

HashMap的computeIfPresent示例产生的输出是:

computeIfPresent之前的映射 = {null=100, 1=10, 2=20, 10=null}
computeIfPresent之后的映射 = {null=null100, 1=110, 2=220, 10=null}
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("compute之前的映射 = "+map);
        for (String key : map.keySet()) {
            map.compute(key, (k,v) -> {return k+v;});
        }
        map.compute("5", (k,v) -> {return k+v;}); // 键不存在,v = null
        System.out.println("compute之后的映射 = "+map);
    }

}

HashMap计算示例的输出是:

compute之前的映射 = {null=10, 1=1, 2=2, 10=null}
compute之后的映射 = {null=null10, 1=11, 2=22, 5=5null, 10=10null}

Java HashMap 的 merge 方法

如果指定的键不存在或与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();
			// 如果键或值为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;}); // 键不存在
		System.out.println(map);
		
		map.merge("1", "1", (k, v) -> {return null;}); // 方法返回null,因此删除该条目
		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的全部内容,希望没有遗漏任何重要信息。如果您觉得这篇文章有帮助,请与他人分享。参考资料:Java API文档。

bannerAds