Java HashMap – Java中的HashMap
Java HashMap是Java中最受欢迎的Collection类之一。
Java HashMap是基于哈希表的实现。
Java中的HashMap扩展了实现Map接口的AbstractMap类。
Java HashMap
关于Java中的HashMap的一些重点是:
Java HashMap允许空键和空值。
HashMap不是有序集合。
您可以通过键集遍历HashMap条目,但是不能保证它们按其添加到HashMap的顺序。HashMap几乎与Hashtable相似,但它是不同步的,并且允许空键和空值。
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 <?扩展K,?扩展V> m):创建一个具有与指定地图相同的映射并且负载因子为0.75的Map
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):如果值存在,则此HashMap方法返回true,否则返回false。
公共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 <?扩展K,?扩展V> m):将所有映射从指定映射复制到此映射。
这些映射将替换该映射对指定映射中当前存在的任何键的任何映射。public V remove(Object key):如果存在,则从此映射中删除指定键的映射。
public int size():返回此映射中的键值映射数。
public Collection <V> values():返回此映射中包含的值的Collection视图。
集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。
Java 8中引入了HashMap中的许多新方法。
public V computeIfAbsent(K key,Function <?super K,?extended V> mappingFunction):如果指定的键尚未与值关联(或者映射为null),则此方法尝试使用给定的映射来计算其值函数,除非Null,否则将其输入到HashMap中。
公用V computeIfPresent(K键,BiFunction <?超级K,?映射值。
公共V计算(K键,BiFunction <?super K,?super V ,?扩展V> remappingFunction):此HashMap方法尝试计算指定键及其当前映射值的映射。
public void forEach(BiConsumer <?super K,?super V>操作):此方法对映射中的每个条目执行给定的操作。
public V getOrDefault(Object key,V defaultValue):与get相同,不同之处在于,如果未找到指定键的映射关系,则返回defaultValue。
公共V merge(K键,V值,BiFunction <?super V,?super V,?extended V> remappingFunction):如果指定的键尚未与值关联或者与null关联,则将其与给定的non关联-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键,V值):仅当指定键当前映射到某个值时,才替换该条目。
public void replaceAll(BiFunction <?super K,?super V,?extends V>函数):将每个条目的值替换为在该条目上调用给定函数的结果。
Java HashMap示例
这是用于HashMap常用方法的简单Java程序。
package com.theitroad.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使用其内部类Node <K,V>来存储映射。
HashMap使用哈希算法,并在密钥上使用hashCode()和equals()方法进行获取和放置操作。
HashMap使用单链表来存储元素,这些元素称为箱或者桶。
当我们调用put方法时,key的hashCode用于确定将用于存储映射的存储桶。
标识存储桶后,将使用hashCode来检查是否已经存在具有相同hashCode的密钥。
如果存在具有相同hashCode的键,则对键使用equals()方法。
如果equals返回true,则值将被覆盖,否则对该单链接列表存储桶进行新映射。
如果没有具有相同hashCode的键,则将映射插入到存储桶中。
对于HashMap get操作,再次使用键hashCode来确定要查找值的存储桶。
标识存储桶后,使用hashCode和equals方法遍历条目以找出条目。
如果找到匹配项,则返回value,否则返回null。
涉及更多的事情,例如散列算法以获取密钥的存储桶,重新哈希映射等。
但是对于我们的工作,请记住,HashMap操作对Key起作用,并且hashCode的良好实现,并且equals方法是必需的,以避免不必要的行为。
下图显示了get和put操作的说明。
Java HashMap负载因子
加载因子用于确定何时重新哈希哈希映射和增加存储桶大小。
铲斗或者容量的默认值为16,负载系数为0.75。
通过将容量和负载因子相乘来计算用于重新哈希处理的阈值。
因此,默认阈值将为12。
因此,当HashMap具有超过12个映射时,它将被重新映射,并且bin的数量将增加到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不由map支持的方法,该怎么办。
package com.theitroad.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值
Java HashMap values方法返回Map中值的Collection视图。
此集合由HashMap支持,因此HashMap中的任何更改都将反映在值集合中,反之亦然。
下面的一个简单示例确认了HashMap值收集的这种行为。
package com.theitroad.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支持,因此映射中的任何更改都会反映在条目集中,反之亦然。
请看下面的HashMap entrySet示例示例程序。
package com.theitroad.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.theitroad.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.theitroad.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.theitroad.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
HashMap的computeIfAbsent方法仅在键不存在于映射中时才计算该值。
计算完值后,如果不为空,则将其放入地图中。
package com.theitroad.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,则删除映射。
package com.theitroad.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计算
如果您要根据函数的键和值在所有映射上应用函数,则应使用计算方法。
如果没有映射并且使用此方法,则计算功能的值将为null。
package com.theitroad.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.theitroad.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}