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))时间。

