HashMap 是基于哈希表的Map 接口实现。它将条目存储在键值对中。它将键映射到值。它是最常用的Collection之一。
HashMap
Map
null
您是否注意到 HashMap 实现了 Map 接口,即使 AbstractMap 已经实现了它? 是的,只是为了让事情更明显,HashMap再次实现了Map接口,再次实现接口并没有错。你不必通过类Hierarchy发现HashMap实现了Map接口。
Java HashMap 类有四个构造函数 public HashMap():这是默认构造函数,主要使用。它创建一个空的 HashMap,默认初始容量为 16,加载因子为 0.75。 public HashMap(int initialCapacity):该构造函数用于指定HashMap的初始容量,默认加载因子0.75。 public HashMap(int initialCapacity,float loadFactor):该构造函数用于指定HashMap的初始容量和加载因子。在大多数情况下,您应该避免使用此构造函数,除非您确定这一点,因为负载因子 0.75 提供了时间和空间之间的良好折衷。 public HashMap(Map<? extends K,? extends V> m):当您想从其他 Map(例如TreeMap或LinkedHashMap)创建 HashMap 时,使用此构造函数。
public HashMap()
public HashMap(int initialCapacity)
public HashMap(int initialCapacity,float loadFactor)
public HashMap(Map<? extends K,? extends V> m)
我们可以使用put()方法将条目添加到HashMap. 例子:
put()
package org.arpit.java2blog; package org.arpit.java2blog; import java.util.HashMap; public class HashMapBuiltMain { public static void main(String[] args) { HashMap<Integer, String> employeeHashmap = new HashMap<Integer, String>(); // Putting key-values pairs in HashMap employeeHashmap.put(1, "Arpit"); employeeHashmap.put(2, "John"); employeeHashmap.put(3, "Martin"); employeeHashmap.put(4, "Vaibhav"); System.out.println(employeeHashmap); } }
当你运行上面的程序时,你会得到下面的输出
{1=Arpit, 2=John, 3=Martin, 4=Vaibhav}
如果您只想在 HashMap 中不存在条目时才添加条目怎么办? 您可以putIfAbsent()在这种情况下使用该方法。
putIfAbsent()
有两种方法可以删除 HashMap 中的条目。
remove(Object key)
remove(Object key,Object value)
package org.arpit.java2blog.HashMap; import java.util.HashMap; public class HashMapRemoveMain { public static void main(String[] args) { HashMap<String, Integer> vehicleMaxSpeedMap = new HashMap<String, Integer>(); // Putting key-values pairs in HashMap vehicleMaxSpeedMap.put("Bike", 120); vehicleMaxSpeedMap.put("Car", 220); vehicleMaxSpeedMap.put("Truck", 160); vehicleMaxSpeedMap.put("Activa", 140); System.out.println(vehicleMaxSpeedMap); // Remove truck key Integer speed = vehicleMaxSpeedMap.remove("Truck"); System.out.println("==============================="); System.out.println("Vehicle Truck with max speed "+speed+" removed from HashMap"); System.out.println(vehicleMaxSpeedMap); System.out.println("================================"); // Remove Car if value is 200 boolean isCarRemoved = vehicleMaxSpeedMap.remove("Car",200); // Car key won't be removed as associated value is 220 System.out.println("Did car removed from HashMap: "+isCarRemoved); System.out.println(vehicleMaxSpeedMap); System.out.println("==============================="); // Remove Car if value is 200 boolean isActivaRemoved = vehicleMaxSpeedMap.remove("Activa",140); // Activa key will be removed as associated value is 140 System.out.println("Did activa removed from HashMap: "+isActivaRemoved); System.out.println(vehicleMaxSpeedMap); System.out.println("==============================="); } }
输出:
{Car=220, Activa=140, Truck=160, Bike=120} =============================== Vehicle Truck with max speed 160 removed from HashMap {Car=220, Activa=140, Bike=120} ================================ Did car removed from HashMap: false {Car=220, Activa=140, Bike=120} =============================== Did activa removed from HashMap: true {Car=220, Bike=120} ===============================
get():从 HashMap 中检索值 put():将值放入 HashMap isEmpty:检查 HashMap 是否为空。 containsKey():检查存在的键是否为 HashMap containsValue():检查 HashMap 中是否存在值:检查 HashMap size()的大小 clear():从 Hashmap 中删除所有元素 clone():它创建 HashMap 的浅表副本。
get()
isEmpty
containsKey()
containsValue()
size()
clear()
clone()
这是一个涵盖这些方法的示例。
package org.arpit.java2blog.HashMap; import java.util.HashMap; public class HashMapMethodsMain { public static void main(String[] args) { HashMap<String, String> employeeDeptmap = new HashMap<>(); //check if map is empty boolean empty = employeeDeptmap.isEmpty(); System.out.println("is employeeDeptmap empty: "+empty); // Putting key-values pairs in HashMap employeeDeptmap.put("Arpit","Tech"); employeeDeptmap.put("John", "Sales"); employeeDeptmap.put("Martin", "HR"); employeeDeptmap.put("Vaibhav","Tech"); System.out.println(employeeDeptmap); //check size of map System.out.println("size of employeeDeptmap: "+employeeDeptmap.size()); // get value from HashMap System.out.println("Martin's department: "+employeeDeptmap.get("Martin")); // Robin's department will be null as we don't have key as "Robin" System.out.println("Robin's department: "+employeeDeptmap.get("Robin")); if(employeeDeptmap.containsKey("John")) { System.out.println("employeeDeptmap has John as key"); } if(employeeDeptmap.containsValue("Sales")) { System.out.println("employeeDeptmap has Sales as value"); } // Removing all entries from Map employeeDeptmap.clear(); System.out.println(employeeDeptmap); } }
is employeeDeptmap empty: true {Arpit=Tech, John=Sales, Martin=HR, Vaibhav=Tech} size of employeeDeptmap: 4 Martin’s department: HR Robin’s department: null employeeDeptmap has John as key employeeDeptmap has Sales as value {}
Java 8的 Map接口引入了新方法,例如帮助您使用lambda 表达式]compute(), computeIfPresent() and computeIfAbsent()编写代码。
compute(), computeIfPresent() and computeIfAbsent()
假设您有一个团队的 HashMap 而没有。的目标如下。
HashMap<String,Integer> teamGoalMap=new HashMap<>(); teamGoalMap.put("team1",1); teamGoalMap.put("team2",1);
现在您要添加 1 个目标,team1并且通常按如下方式进行。
team1
teamGoalMap.put("team1",teamGoalMap.get("team1")+1);
相反,您可以使用以下计算轻松完成此操作。
teamGoalMap.compute("team1",(team,goal) ->goal+1);
因此,每当您想基于键值对应用映射时,都应该使用计算。
如果指定的键存在且值不为空,则 computeIfPresent 重新计算值。 您之前可能编写过如下代码:
if(teamGoalMap.containsKey("team1")) { teamGoalMap.put("team1",teamGoalMap.get("team1")+1); }
你可以用computeIfPresent如下方式重写它
computeIfPresent
teamGoalMap.computeIfPresent("team1",(team,goal) ->goal+1);
如果函数返回 null,则键将从 HashMap 中删除。
如果指定的键不存在且函数不返回 null,则 computeIfAbsent 重新计算值。 您之前可能编写过如下代码:
if(!teamGoalMap.containsKey("team3")) { teamGoalMap.put("team3",0); }
你可以用computeIfAbsent如下方式重写它
computeIfAbsent
teamGoalMap.computeIfAbsent("team3",value -> 0);
如果 key 已经存在于 map 中,那么什么都不会改变。
让我们看另一个以声明式样式重写 HashMap 代码的示例。
您可能已经以一种简单的方式编写了程序,如下所示。
package org.arpit.java2blog.HashMap; import java.util.HashMap; public class FrequencyOfEachWord { public static void main(String[] args) { String str = "thisisjavablog"; HashMap<Character,Integer> hMap = new HashMap<>(); for(int i= 0 ; i< str.length() ; i++) { Character c=str.charAt(i); if(hMap.containsKey(c)) { int count = hMap.get(c); hMap.put(c,count+1); } else { hMap.put(c,1); } } System.out.println(hMap); } }
{a=2, b=1, s=2, t=1, v=1, g=1, h=1, i=2, j=1, l=1, o=1}
上面的程序使用简单的逻辑来计算字符串中每个字符的频率。
computeIfPresent让我们使用andcomputeIfAbesent方法重写这个逻辑。
computeIfAbesent
package org.arpit.java2blog.HashMap; import java.util.HashMap; public class FrequencyOfEachWord { public static void main(String[] args) { String str = "thisisjavablog"; HashMap<Character,Integer> hMap = new HashMap<>(); for(int i= 0 ; i< str.length() ; i++) { Character c=str.charAt(i); hMap.computeIfPresent(c, (character,count)-> count+1); hMap.computeIfAbsent(c, (character)-> 1); } System.out.println(hMap); } }
如您所见,在computeIfPresent和computeIfAbesent方法的情况下,逻辑看起来非常可读。
entrySet()`: 由于 HashMap 以 的形式存储键值对`Entry`,我们可以通过调用来检索 entrySet()`map.entrySet()
keySet():提供一组键。
keySet()
values():提供值的集合。
values()
这是相同的示例。
package org.arpit.java2blog; import java.util.Collection; import java.util.HashMap; import java.util.Map.Entry; import java.util.Set; public class HashMapEntrySetMain { public static void main(String[] args) { HashMap<Integer, String> studentIDNameMap = new HashMap<>(); // Putting key-values pairs in HashMap studentIDNameMap.put(101,"Andy"); studentIDNameMap.put(102, "Mary"); studentIDNameMap.put(103, "Sam"); studentIDNameMap.put(104,"Sandy"); // get entrySet Set<Entry<Integer, String>> entrySet = studentIDNameMap.entrySet(); System.out.println("EntrySet: "+entrySet); // get keySet Set<Integer> keySet = studentIDNameMap.keySet(); System.out.println("keySet: "+keySet); // get values Collection<String> values = studentIDNameMap.values(); System.out.println("values: "+values); } }
EntrySet: [101=Andy, 102=Mary, 103=Sam, 104=Sandy] keySet: [101, 102, 103, 104] values: [Andy, Mary, Sam, Sandy]
有很多方法可以遍历 HashMap
keyset()
entrySet()
iterator
package org.arpit.java2blog.HashMap; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; public class HashMapIterationMain { public static void main(String[] args) { HashMap<String, String> userCountryMap = new HashMap<>(); // Putting key-values pairs in HashMap userCountryMap.put("Anchit","India"); userCountryMap.put("Andy", "USA"); userCountryMap.put("Roy", "Germary"); userCountryMap.put("Mary","France"); System.out.println("========================================================="); System.out.println("Iterating over HashMap with foreach and lambda:"); userCountryMap.forEach((user,country) -> { System.out.println(user+" --> "+country); } ); System.out.println("========================================================="); System.out.println("Iterating over HashMap using keyset() with foreach loop:"); for(String user:userCountryMap.keySet()) { System.out.println(user+" --> "+userCountryMap.get(user)); } System.out.println("========================================================="); System.out.println("Iterating over HashMap's keyset() with foreach and lambda:"); userCountryMap.keySet().forEach((user) -> { System.out.println(user+" --> "+userCountryMap.get(user)); } ); System.out.println("========================================================="); System.out.println("Iterating over HashMap's entrySet with iterator"); Iterator<Entry<String, String>> iterator = userCountryMap.entrySet().iterator(); while(iterator.hasNext()) { Entry<String, String> next = iterator.next(); System.out.println(next.getKey()+" --> "+next.getValue()); } System.out.println("========================================================="); System.out.println("Iterating over HashMap's entrySet with foreach and lambda"); userCountryMap.entrySet().forEach((entry) -> { System.out.println(entry.getKey()+" --> "+entry.getValue()); } ); System.out.println("========================================================="); System.out.println("Iterating over HashMap's entrySet with foreach loop"); for(Map.Entry<String, String> entry:userCountryMap.entrySet()) { System.out.println(entry.getKey()+" --> "+entry.getValue()); } } }
========================================================= Iterating over HashMap with foreach and lambda: Anchit –> India Andy –> USA Roy –> Germary Mary –> France ========================================================= Iterating over HashMap using keyset() with foreach loop: Anchit –> India Andy –> USA Roy –> Germary Mary –> France ========================================================= Iterating over HashMap’s keyset() with foreach and lambda: Anchit –> India Andy –> USA Roy –> Germary Mary –> France ========================================================= Iterating over HashMap’s entrySet with iterator Anchit –> India Andy –> USA Roy –> Germary Mary –> France ========================================================= Iterating over HashMap’s entrySet with foreach and lambda Anchit –> India Andy –> USA Roy –> Germary Mary –> France ========================================================= Iterating over HashMap’s entrySet with foreach loop Anchit –> India Andy –> USA Roy –> Germary Mary –> France
您可以将自定义对象存储为 HashMap 中的 Key,但您应该实现hashcode 和 equals方法,否则它可能无法按预期工作。
创建一个名为Country.java
Country.java
package org.arpit.java2blog; public class Country { String name; long population; public Country(String name, long population) { super(); this.name = name; this.population = population; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getPopulation() { return population; } public void setPopulation(long population) { this.population = population; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + (int) (population ^ (population >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Country other = (Country) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } @Override public String toString() { return "Country: "+name+" | population:"+population; } }
创建另一个类HashMapMain.java
HashMapMain.java
package org.arpit.java2blog; import java.util.HashMap; public class HashMapMain { public static void main(String args[]) { Country india=new Country("India",10000); Country japan=new Country("Japan",3000); Country france=new Country("France",5000); Country russia=new Country("Russia",4000); // HashMap with Country name as key and capital as value // HashMap stores elements in key value pairs HashMap<Country,String> countryCapitalMap=new HashMap<>(); countryCapitalMap.put(india,"Delhi"); countryCapitalMap.put(japan,"Tokyo"); countryCapitalMap.put(france,"Paris"); countryCapitalMap.put(russia,"Moscow"); System.out.println("-----------------------------"); // Iterating HashMap Using keySet() and for each loop System.out.println("Iterating HashMap Using keySet() and for each loop"); for (Country countryKey:countryCapitalMap.keySet()) { System.out.println(countryKey +" and Capital:"+countryCapitalMap.get(countryKey)); } System.out.println("-----------------------------"); } }
—————————– Iterating HashMap Using keySet() and for each loop Country: Japan | population:3000 and Capital:Tokyo Country: India | population:10000 and Capital:Delhi Country: Russia | population:4000 and Capital:Moscow Country: France | population:5000 and Capital:Paris —————————–
我们可以使用TreeMap对 HashMap 中的键进行排序。我们只需要将 HashMap 传递给 TreeMap 的构造函数。
我们需要按照以下步骤按值对 HashMap 进行排序。
List
让我们写一个例子来按键和值对 HashMap 进行排序。我们将创建一个名为 Vehicle 的类,并将其用作 HashMap 中的 Key,而 value 将是 Vehicle 的所有者。
创建一个名为的类Vehicle.java
Vehicle.java
package org.arpit.java2blog; public class Vehicle implements Comparable<Vehicle>{ String name; long maxSpeed; public Vehicle(String name, long maxSpeed) { super(); this.name = name; this.maxSpeed = maxSpeed; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getMaxSpeed() { return maxSpeed; } public void setMaxSpeed(long maxSpeed) { this.maxSpeed = maxSpeed; } @Override public String toString() { return "Vehicle name: "+name+"|Max speed: "+maxSpeed; } @Override public int compareTo(Vehicle v) { return this.getName().compareTo(v.getName()); } }
请注意,我们在这里实现了可比较的接口,它通过名称比较两辆车。这Comparable将用于在构造TreeMap时按 Keys 对其进行排序。 创建一个名为的类HashMapSortMain.java
Comparable
HashMapSortMain.java
package org.arpit.java2blog; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; public class HashMapSortMain { public static void main(String[] args) { Vehicle v1=new Vehicle("Car", 150); Vehicle v2=new Vehicle("Truck", 130); Vehicle v3=new Vehicle("Bike", 150); Vehicle v4=new Vehicle("Jeep", 180); // HashMap stores elements in key value pairs HashMap<Vehicle,String> vehicleOwnerMap=new HashMap<>(); vehicleOwnerMap.put(v1,"John"); vehicleOwnerMap.put(v2,"Chris"); vehicleOwnerMap.put(v3,"Mary"); vehicleOwnerMap.put(v4,"Harry"); // Sort by keys // As Vehicle class implements Comparable which defines sorting by vehicle name TreeMap<Vehicle,String> treeMap=new TreeMap<Vehicle,String>(vehicleOwnerMap); System.out.println("Sorted TreeMap by vehicle name: "+treeMap); // Sort by values Set<Entry<Vehicle, String>> entrySet = vehicleOwnerMap.entrySet(); List<Entry<Vehicle, String>> vehicleEntryList=new ArrayList<>(entrySet); Collections.sort(vehicleEntryList,(e1,e2) -> e1.getValue().compareTo(e2.getValue())); LinkedHashMap<Vehicle, String> lmp=new LinkedHashMap<Vehicle, String>(); vehicleEntryList.forEach((entry)-> { lmp.put(entry.getKey(), entry.getValue()); }); System.out.println("Sorted Map by owner name: "+lmp); } }
Sorted TreeMap by vehicle name: {Vehicle name: Bike|Max speed: 150=Mary, Vehicle name: Car|Max speed: 150=John, Vehicle name: Jeep|Max speed: 180=Harry, Vehicle name: Truck|Max speed: 130=Chris}Sorted Map by owner name: {Vehicle name: Truck|Max speed: 130=Chris, Vehicle name: Jeep|Max speed: 180=Harry, Vehicle name: Car|Max speed: 150=John, Vehicle name: Bike|Max speed: 150=Mary}
默认情况下 HashMap 不是线程安全的,在多线程环境中它会给出不确定的结果。 让我们借助一个示例来演示这一点:
package org.arpit.java2blog; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class Counter { public static void main(String[] args) throws InterruptedException { Map<String, Integer> counterMap=new HashMap<>(); counterMap.put("counter1",0); counterMap.put("counter2",100); ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10); Runnable counterTask = () -> { incrementTime(counterMap,"counter1"); incrementTime(counterMap,"counter2"); }; for (int i = 1; i <= 100; i++) { newFixedThreadPool.submit(counterTask); } newFixedThreadPool.shutdown(); newFixedThreadPool.awaitTermination(30, TimeUnit.SECONDS); System.out.println("Time for Counter1: "+counterMap.get("counter1")); System.out.println("Time for Counter2: "+counterMap.get("counter2")); } public static void incrementTime(Map<String,Integer> counterMap,String counter) { Integer count = counterMap.get(counter) count++; counterMap.put(counter,count); } }
我在 map 中放置了两个条目,键分别为 counter1 和 counter2,值分别为时间 0 和 100。我们创建了一个任务,将两个计数器的时间都增加 1,并且我们使用ExecuterService提交 100 次。
让我们运行程序并检查输出:
Time for Counter1: 95 Time for Counter2: 195
但我们的预期输出应该是
Time for Counter1: 100 Time for Counter2: 200
因为我们已经提交了 100 次任务,并且在每个任务执行中,它都会调用 incrementTime ( ) 并将时间增加 1。 让我们再次运行程序。
Time for Counter1: 98 Time for Counter2: 197
它与上次执行不同,这是由于 HashMap 中的线程安全问题。
我们可以通过两种方式解决这个线程安全问题:
我们可以使用Collections.synchronizedMap()使HashMap线程的所有操作安全,并使 incrementTime ( ) 方法同步来解决上述问题。 incrementTime ( ) 也应该同步,否则会有原子性问题。
Collections.synchronizedMap()
package org.arpit.java2blog.HashMap; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class Counter { public static void main(String[] args) throws InterruptedException { Map<String, Integer> counterMap=Collections.synchronizedMap(new HashMap<>()); counterMap.put("counter1",0); counterMap.put("counter2",100); ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10); Runnable counterTask = () -> { incrementTime(counterMap,"counter1"); incrementTime(counterMap,"counter2"); }; for (int i = 1; i <= 100; i++) { newFixedThreadPool.submit(counterTask); } newFixedThreadPool.shutdown(); newFixedThreadPool.awaitTermination(30, TimeUnit.SECONDS); System.out.println("Time for Counter1: "+counterMap.get("counter1")); System.out.println("Time for Counter2: "+counterMap.get("counter2")); } public static synchronized void incrementTime(Map<String,Integer> counterMap,String counter) { Integer count = counterMap.get(counter); count++; counterMap.put(counter,count); } }
如您所见,我们在使用Collections.synchronizedMap()和incrementTime同步后得到了正确的输出。
incrementTime
使用 Collections.synchronizedMap() 的缺点是它会锁定整个 hashmap 对象,这可能会导致性能问题,但ConcurrentHashMap只锁定映射的一部分并且在多线程环境中表现得很好。
要了解这种变化,您需要了解 HashMap 内部的工作原理。Java 8在哈希冲突过多的情况下引入了良好的性能改进。
在 Java 7 之前 ,如果两个对象具有相同的 hashcode 并且不相等,那么两者将在单链表bucket的帮助下存储在同一位置。如果有太多的哈希冲突,那么性能可能会受到影响。 在最坏的情况下,如果所有键都具有相同的哈希码,则HashMap 中的操作可能需要O(n) 时间。get()``put() get()
bucket
get()``put()
Java 8 更新在 Java 8中,如果元素数量达到某个阈值 ,HashMap 会将链表更改为二叉树。在此更改的帮助下,HashMap 中的 get() 操作在最坏的情况下可能需要O(log(n)) 时间。
结论
您已经了解了 HashMap 的基础知识、如何创建 HashMap 并向其添加键值对、重要的 HashMap 方法、如何使用 HashMap 编写声明式代码、如何迭代 HashMap 和 HashMap 的线程安全问题以及如何同步一个哈希映射。
这就是java中的HashMap。
原文链接:https://codingdict.com/