java的hashmap.
HashMap是基于哈希表的实现 Map
接口。
它存储在键值对中的条目。
它将键映射到值。
它是最常用的集合之一。
Java Hashmap.
HashMap
实施Map
映射到值键的接口。- 它不同步,并且不是线程安全的。
- 不允许重复键
- 一
null
钥匙和多个null
允许值 - 它是无序的集合,并不为任何特定元素的保证提供保证。
您是否注意到: 即使AbstractMap已经实现了它,HashMap实现了Map接口?
》 是的,为了让事情变得更明显,HashMap再次实现了Map接口,实现接口并没有错又来了。你呢不必遍历类层次结构就可以发现HashMap实现了Map接口。
Hashmap构造函数
Java HashMap类有四个构造函数
public 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)
:当我们希望从Treemap或者LinkedHashMap等其他地图中创建HashMap时使用此构造函数。
将键值对添加到HashMap
我们可以用 put()
添加条目的方法 HashMap
。
例子:
package org.igi.theitroad; package org.igi.theitroad; 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, "igi"); employeeHashmap.put(2, "John"); employeeHashmap.put(3, "Martin"); employeeHashmap.put(4, "Vaibhav"); System.out.println(employeeHashmap); } }
运行上面的程序时,我们将得到以下输出
{1=igi, 2=John, 3=Martin, 4=Vaibhav}
如果只有在HashMap中尚未存在时,才能添加条目,该怎么办?
我们可以使用 putIfAbsent()
这种情况下的方法。
删除hashmap的条目
有两种方法可以在HashMap中删除条目。
remove(Object key)
:它从hashmap删除密钥remove(Object key,Object value)
:如果值与传递参数值相同,则会删除键。
package org.igi.theitroad.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("==============================="); } }
重要的HashMap方法
get()
:从HashMap检索值put()
:将值放入HashMap中isEmpty
:检查HashMap是否为空。containsKey()
:检查键存在是否是HashMapcontainsValue()
:检查hashmap中是否存在值size()
:检查HashMap的大小clear()
:删除HashMap的所有元素clone()
:它创造了HashMap的浅副本。
以下是涵盖这些方法的示例。
package org.igi.theitroad.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("igi","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); } }
使用hashmap写入语句式样式代码
Java 8的地图接口推出了新方法,如
compute(), computeIfPresent() and computeIfAbsent()
哪些使用lambda表达式编写代码。
compute()
让我们说你有一个团队的HashMap和没有。
目标如下。
HashMap<String,Integer> teamGoalMap=new HashMap<>(); teamGoalMap.put("team1",1); teamGoalMap.put("team2",1);
现在你想加一个目标 team1
通常,你这样做如下。
teamGoalMap.put("team1",teamGoalMap.get("team1")+1);
相反,我们可以轻松地使用以下计算。
teamGoalMap.compute("team1",(team,goal) ->goal+1);
因此,无论何时要基于键应用映射,那么应该使用价值对。
computeifpresent()
ComputeIfPresent如果存在指定的键,则重新计算值,并且值不是null。
我们之前可能有以下类似的代码:
if(teamGoalMap.containsKey("team1")) { teamGoalMap.put("team1",teamGoalMap.get("team1")+1); }
你可以重写这个 computeIfPresent
如下
teamGoalMap.computeIfPresent("team1",(team,goal) ->goal+1);
如果函数返回null,则将从hashmap中删除键。
computeifabsent()
ComputeIfabsent重新计算如果未存在指定的键并且函数不会返回NULL,则该值将重新计算该值。
我们之前可能有以下类似的代码:
if(!teamGoalMap.containsKey("team3")) { teamGoalMap.put("team3",0); }
你可以重写这个 computeIfAbsent
如下
teamGoalMap.computeIfAbsent("team3",value -> 0);
如果键已在地图中存在,那么什么都不会改变。
让我们看到另一个例子来以声明方式重写HashMap代码。
问题:我们要在字符串中找到每个字符的频率。
我们可能会以以下方式编写该程序。
package org.igi.theitroad.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); } }
上面的程序使用简单的逻辑来计算字符串中每个字符的频率。
- 创建一个包含字符以计算映射的HashMap。
- 字符迭代字符串字符
- 如果地图中不存在字符,则计数应为1
- 如果角色已经存在于地图中,则将其计数递增1
让我们使用这个逻辑 computeIfPresent
和 computeIfAbesent
方法。
package org.igi.theitroad.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
方法。
获取hashmap的entryset(),keyset()和values()
entryset()
entrySet()
:哈希图以形式存储关键值对Entry
,我们可以通过调用来检索entryset() map.entrySet()
keyset()
keySet()
:提供一组密钥。
values()
values()
:提供一个值的集合。
这是一个例子。
package org.igi.theitroad; 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); } }
迭代Hashmap.
有很多方法可以迭代HashMap
- 迭代HashMap使用
keyset()
- 迭代HashMap使用
keyset()
使用foreach()和lambda表达[Java 8] - 使用foreach()和lambda表达式迭代hashmap [Java 8]
- 迭代HashMap的
entrySet()
使用iterator
- 迭代HashMap的
entrySet()
使用foreach()和lambda表达式[Java 8] - 迭代HashMap的
entrySet()
使用foreach循环
package org.igi.theitroad.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","Netherlands"); 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()); } } }
将自定义对象存储为键
我们可以将自定义对象存储为HashMap中的键,但我们应该实现HashCode并等于方法,否则可能无法按预期工作。
创建一个叫做 Country.java
package org.igi.theitroad; 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
package org.igi.theitroad; import java.util.HashMap; public class HashMapMain { public static void main(String args[]) { Country Netherlands=new Country("Netherlands",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(Netherlands,"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("-----------------------------"); } }
在java中排序hashmap
按键排序
我们可以使用Treemap对HashMap中的键进行排序。
我们只需要将HashMap传递给Treemap的构造函数。
按值排序
我们需要遵循以下步骤来按值对HashMap进行排序。
- 得到
entrySet()
来自Hashmap. - 将entryset转换为
List
- 在比较器的帮助下对列表进行排序
- 迭代列表并将条目对象放在LinkedHashMap中
让我们编写一个例子来按键和值对HashMap进行排序。
我们将创建一个名为车辆的类,并将其用作HashMap中的键,值将是车辆的所有者。
创建一个名为的类 Vehicle.java
package org.igi.theitroad; 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时按键对其进行排序。
创建一个名为的类 HashMapSortMain.java
package org.igi.theitroad; 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); } }
如果我们不明白我们在上面使用的Lambda表达式的语法来对输入对象的排序列表,则需要通过Java 8中的Lambda表达式。
Hashmap线程安全吗?
默认情况下,HashMap不是线程安全的,并且在多线程环境的情况下,它可以给出非确定性结果。
让我们在一个例子的帮助下演示这一点:
package org.igi.theitroad; 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中将两个条目放在MAP中,键分别为COURCT1和COUNTER2,并且值分别为时间0和100.WE创建了一个任务,该任务将逆1递增1,并且我们正在使用ExecuterService将其提交100次。
让我们运行程序并检查输出:
计数器的时间1:逆2:195的95次
但我们的预期输出应该是
计数器的时间1:逆2:200的100次
由于我们提交了100次并在每个任务执行中提交了任务,所以它调用incrementTime()并增加时间1.让我们再次运行程序。
Time for Counter1: 98 Time for Counter2: 197
它与上次执行不同,这是由于HashMap中的线程安全问题。
我们可以以两种方式解决此主题安全问题:
- CollectionS.SynchronizeMap.
- concurrenthashmap.
CollectionS.SynchronizeMap.
我们可以用 Collections.synchronizedMap()
要使HashMap线程的所有操作安全,并使IncrementTime()方法同步以解决上述问题。
递增时间()也应该同步,否则会有明确的问题。
package org.igi.theitroad.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
同步。
concurrenthashmap.
使用集合的缺点。
HashMap如何在内部工作
此主题值得单独的帖子,因此我已经写了完整的教程,就HashMap如何在Java中工作。
Java 8 HashMap更新
要了解这一变化,我们需要了解HashMap在内部工作原理。
在太多哈希碰撞的情况下,Java 8引入了良好的性能改进。
在Java 7之前,如果两个对象具有相同的哈希码并且不等于,则两者都将存储在相同 bucket
在单独的名单的帮助下。
如果有太多的哈希碰撞,那么性能 get()
和 put()
可能会受苦。
在最坏的情况下,如果所有键都具有相同的哈希码 get()
HashMap的操作可能需要O(n)时间。
Java 8在Java 8中更新,HashMap将链接列表更改为二进制树,以便在元素数量达到某个阈值时。
在此更改的帮助下,HashMap中的Get()操作可能需要在最坏情况下o(log(n))时间。